代数数据类型与平等

时间:2014-11-23 19:42:45

标签: haskell equality functor

鉴于来自TypeClassopedia的以下数据类型:

data Cons a = Cons a (Cons a) | Empty deriving (Show, Eq)

我实现了它的邪恶Functor实现:

instance Functor Cons where
    fmap _ Empty       = Empty
    fmap f (Cons x xs) = Cons (f x) (Cons (f x) (fmap f xs) )

然后,我尝试编写一个函数(quickcheck属性),它接受Cons a并返回Bool

prop_id_functor_law :: Cons a -> Bool
prop_id_functor_law x = fmap id x == id x

但是,我收到编译时错误:

Prelude> :l EvilFunctor
[1 of 1] Compiling EvilFunctor      ( EvilFunctor.hs, interpreted )

EvilFunctor.hs:18:23:
    No instance for (Eq a) arising from a use of `=='
    Possible fix:
      add (Eq a) to the context of
        the type signature for prop_id :: Cons a -> Bool
    In the expression: fmap id x == id x
    In an equation for `prop_id': prop_id x = fmap id x == id x
Failed, modules loaded: none.

我粗略的直觉是这个编译时错误是有道理的。两个a如何比较,除非他们实现了Eq类型类?

但是,当我定义deriving ... Eq时,data Cons a甚至做了什么?

2 个答案:

答案 0 :(得分:4)

它表示您需要将约束添加到prop_id_functor_law,因此它是prop_id_functor_law :: Eq a => Cons a -> Boolderiving部分仅表示它派生实例

instance Eq a => Eq (Cons a) where
    Empty == Empty = True
    Cons a1 x1 == Cons a2 x2 = a1 == a2 && x1 == x2
    _ == _ = False

您仍然需要约束type参数,以便满足Eq实例。如果您要检查GHCi中的:info Cons,您会看到该实例。

答案 1 :(得分:2)

  

deriving ... Eq甚至在我定义数据Cons a时做了什么?

当您派生实例时,GHC始终机械地生成一个实例,该实例实现类型结构的类型所需的逻辑。例如:

data Foo = Foo Int Int
  deriving Eq

会给你类似的东西:

instance Eq Foo
  where Foo a b == Foo a' b'
          = a == a' && b == b'

但如果你改为:

data Foo a b = Foo a b
  deriving Eq

然后它可以告诉它需要遵循相同的结构(如果两个包含的字段相等,则Foo s相等)但是它被强制委托为类型a和{{1部分比较。这些具有相等的类型并不总是这样,因此派生实例必须声明它们作为约束:

b

instance (Eq a, Eq b) => Eq (Foo a b) where Foo a b == Foo a' b' = a == a' && b == b' 也会发生同样的事情。因此,当值相等于时,两个Cons值可用于等式。声明Cons a这样的函数适用于所有prop_id_functor_law :: Cons a -> Bool值,无论Cons a是否成立,因此类型检查器不允许您在{{1}上调用Eq a在实现中;它可能不支持相等性,并且保证您永远不会调用不受支持的操作是类型检查的重点。但是如果你改为==,那么你可以使用Cons a(责任转移到prop_id_functor_law :: Eq a => Cons a -> Bool来电者,以确保他们为支持的类型调用它平等)。