在Haskell中,为什么我需要在实例声明中指定类型类?

时间:2014-06-22 12:33:34

标签: haskell

以下内容无法编译:

data Point a b = Point { x :: a
                       , y :: b
                       } deriving (Show)

instance Eq (Point a b) where
  (Point x _) == (Point y _) = x == y

错误是:

No instance for (Eq a)
  arising from a use of `=='
In the expression: x == y
In an equation for `==': (Point x _) == (Point y _) = x == y
In the instance declaration for `Eq (Point a b)'

但是,如果我将类型类添加到实例中,那么它可以工作:

data Point a b = Point { x :: a
                       , y :: b
                       } deriving (Show)

instance (Eq a) => Eq (Point a b) where
  (Point x _) == (Point y _) = x == y

编译器是否可以看到我在那里使用a == a并推断出a必须位于类型Eq中?

2 个答案:

答案 0 :(得分:15)

可以表示a必须在类型类Eq中。这正是它抱怨的原因。您声明了instance Eq (Point a b),其中表示Point a b形式的类型位于{em>任何类型Eq和{{1}的a类型类中},但您提供了b的定义,仅当==a的成员时才有效。

这两件事是不一致的,所以Haskell不会试图猜测哪一个是你真正的意思,它只是将其报告为错误。该语言没有 以这种方式工作,但这是一个深思熟虑的设计选择。

答案 1 :(得分:5)

想象一个功能

equals ::  a -> a -> Bool
equals = (==)

编译器显然可以推断出equals的类型应该是Eq a => a -> a -> Bool。但它错了,因为你声明你的类型适用于所有的。

Typeclass实例是类似的,除了我们没有选择让它们以我们可以省略equals的类型声明的方式推断它们。因此,有必要以与指定函数的类型签名相同的方式来规定约束,您还必须指定约束。

也就是说,ghc不会推断约束;它要么必须推断整个签名,要么不推断它。 (嗯,它推断出任何一种方式,但推断必须比你输入的更为通用,如果你输入它 - 并且约束符合这个要求)。

将您的初始instance Eq (Point a b)视为(==) :: (Point a b) -> (Point a b) -> Bool,这是不可能以您喜欢的方式定义的,因为它赋予该主体(==) :: Eq a => (Point a b) -> (Point a b) -> Bool