在 Haskell 中,`==` 的类型应该是 `Eq a b => a -> b -> Bool`。

6

(==) 的类型是 Eq a => a -> a -> Bool,但我可以想象一个更一般化的版本 Eq a b => a -> b -> Bool,当类型不匹配时为 false,当类型匹配时仅为通常的相等性。为什么不这样做呢?


11
你能否举个例子说明在什么情况下可能会使用这个?就我而言,我希望在编译时就知道我是否在提出一个显然可以回答的问题。 - jamshidh
我不知道有任何可以在异构流上工作的解析器组合库。Parsec、attoparsec、polyparse和uu-parsinglib(我使用过的)肯定不行。 - Carl
3
为什么你想要/创建一个异构流?一切不都是以字符串或文本开始的吗?你想将其转换为目标表示,通常是联合类型 - 你的AST。我看不出你需要任何异构内容。 - AndrewC
@davidk01,我不能代表其他人,但我真的不确定你在暗示什么(什么是具有匹配器的异构流?)。所以我并不是反对(或支持)这种方法,我只是不理解你在说什么。我认为提供一个例子会更有帮助。 - David Young
@davidk01 这看起来与我代码中的 54-60 行相等。 - David Young
显示剩余7条评论
4个回答

14

我们可以通过EqData.Typeable来定义它。我们可以比较可Typeable的对象的类型,以确定它们是否为相同类型。

(?==) :: (Eq b, Typeable a, Typeable b) => a -> b -> Bool
x ?== y = cast x == Just y

cast 检查一个 Typeable 类型的值是否与另一个类型相同。如果它们是相同的类型,则返回输入的 Just 值,否则返回Nothing

以下是几个演示期望行为的示例。

> 7 ?== 7
True

> 7 ?== "hello"
False

> 7 ?== 5
False

> (7 :: Int) ?== (7 :: Integer)
False

太棒了。谢谢。我刚想要深入研究这个问题,但看到你的回答就放心了。 - David K.

7
(==) :: Eq a b => a -> b -> Bool并不像你想象的那样有用。要确定2参数Eq的实例,您需要了解两种类型,以便匹配实例。任何调用这个“==”的人都需要明确知道“a”和“b”,或者在他们的接口中传递约束;然后他们自己的调用者需要知道“a”和“b”是什么,或者传递约束……在某个时候,必须选择Eq实例,而不是通过约束提供它,这涉及在编译时知道“a”和“b”。此时,为什么要麻烦呢?如果您知道“a”和“b”是相同的,则单参数Eq足以比较它们。如果您知道它们是不同的,则知道答案是“False”,您不需要任何实例来告诉您。如果您不知道它们是否相同或不同,则根据定义,您不知道足够选择Eq实例的信息,因此根本无法调用“==”(或受限制的函数)。因此,这对于从不同来源获取未知类型(实现相等性)的“a”和“b”,然后进行比较将没有帮助。它们必须以“Eq a b”实例的形式一起出现,这实际上是一个编译时的证明,说明它们是否为同一类型。@Cirdec答案中的Typeable版本非常不同,也更有用。在此,每个类型独立支持Typeable,其中一个支持单参数Eq;您可以从两个不同的源获取这两个值,在那里您不知道它们是否相同,但您知道可以检查每个类型;“a”源必须了解“a”,以选择Typeable实例,“b”源必须了解“b”以选择Typeable和Eq实例,但是没有任何代码需要同时充分了解两种类型以告诉它们是否相同(在编译时)。2参数Eq强制同时了解两种类型,这将使其接近无效。

2
你可能需要提到Cirdec的(不可避免的)Typeable约束并非免费 - 除非它们在编译时被专门化(因此您不需要?==),否则它们涉及在运行时传递两个额外的字典。 - dfeuer

3
我认为,对于你的问题,哲学上的答案是Haskell本质上类似于数学,当我们戴上数学帽子并为数学对象定义相等性时,通常认为这两个对象属于同一类型。例如,看看集合相等是如何定义的。
现在,自然数集是否等于自然数6?或者让我们疯狂一点——椅子是否等于整数-7?虽然我很想让我的直觉接管并尖叫“当然不是!”,但我实际上不能这样说,因为问题本身是无效的,一切都很未定义。集合和自然数之间的相等是未定义的。椅子和整数之间的相等也是未定义的。
这是否意味着你不能定义它?不,可以去定义。
但这只是一种不太流行的方法,所以基础库的选择非常可以理解。
作为对你评论的回复,这是正确的。恰好,集合是如此原始,以至于我们可以用它们来表示几乎任何东西。这只是一个例子,我不知道如果我们选择其他东西是否能够进行类似的论证,但让我们继续下去。
对于集合A等于集合B,我们需要:
  1. 集合A中的每个元素都存在于集合B中
  2. 集合B中的每个元素都存在于集合A中
简单地说,集合{1,2,3}是否等于7?我不知道,因为说一个元素存在于7中是未定义的,问题是无效的。
但让我们按照你的方法做:取7,穿上漂亮的裙子抬起来,并称之为集合C,甚至假设C等于集合D以展示你的正确性。在我们能够这样做之前,我们必须先将其变成一个集合。如果可能的话,这正是Haskell希望你做的。在数学中,几乎总是可以做到这一点。
因此,在Haskell和数学中,我们仍然无法真正地问:
(a :: Set) == (7 :: Natural)

然而,在Haskell和数学中,我们可以先提升它,以便我们可以以不同的方式查看它,然后我们可以问:

(liftN :: Natural -> Set) (7 :: Natural) == (a :: Set)

当然,右手边的集合C就是RHS。

这个答案的重点在于,Haskell通过以常见的数学术语定义相等性的方式,仅仅是在表达惯用语。


1
@davidk01,Haskell更多地基于类型理论而非集合论。 - dfeuer
@davidk01,是的,可能是这样,但您不能指望它是一个特别“好”的同构。 - dfeuer
@davidk01:“在大多数数学中,表示的变换是一种很常见的操作”,在Haskell中也是如此。而且你可以在任何情况下都明确地进行它。 - MasterMastic

2

Haskell的观点是只有同一类型的值之间才有比较相等的意义,也就是说,比较不同类型的值是无意义的,而且很可能意味着你的程序出了问题。这也是为什么(==)的类型会是它现在的样子。

总的来说,这就是在像Haskell这样强大的类型系统中工作的含义。它旨在帮助编写更好、更有意义的程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接