代数数据类型和平等

问题描述:

鉴于从TypeClassopedia以下数据类型:代数数据类型和平等

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

我实现了其罪恶的函子实现:

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

于是,我试图写一个函数(快速检查财产)需要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类型?

然而,当我定义data Cons a时,deriving ... Eq甚至可以做什么?

它说你需要添加约束到prop_id_functor_law,所以它会是prop_id_functor_law :: Eq a => Cons a -> Bool。该deriving部分只是意味着它派生的实例

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

你仍然有类型参数是为了限制为Eq实例得到满足。如果您要在GHCi中检查:info Cons,您会看到该实例。

what did the deriving ... Eq even do when I defined data 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小号如果两个字段都相等,则它们是相等的),但强制将ab的类型委托给相等部分进行比较。这些并不总是平等类型,所以派生的实例有权宣布,他们这样做的制约:

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的值,无论是否为Eq a,所以类型检查器将不允许您在Cons a范围内调用==执行内容;它可能不支持平等,并保证你永远不会调用不受支持的操作是类型检查的关键。但是,如果您改为prop_id_functor_law :: Eq a => Cons a -> Bool,那么您可以使用==(并且责任移动到调用者prop_id_functor_law以确保他们将它称为支持相等的类型)。