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