我试图更好地理解Haskell中的类和实例。因此,我正在调查:
http://learnyouahaskell.com/making-our-own-types-and-typeclasses
在这里,他们建议尝试:
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False
但当我这样做时,我会得到以下输出:
tryouts.hs:58:10: error:
Duplicate instance declarations:
instance Eq m => Eq (Maybe m) -- Defined at tryouts.hs:58:10
instance Eq a => Eq (Maybe a) -- Defined in `GHC.Maybe'
我该如何克服这一点并实例化我自己的类型类声明?
作者认为,课堂前的
(Eq m) => Eq
对约束实例的内容很重要。这明显不同于在类级别(例如(定义Eq
时这样做。然而,我不确定我是否理解其中的区别。这可能是我不理解OOP中类型类和正则类之间的区别以及前者的"继承"性质的地方。
- 如何克服这一点并实例化我自己的类型类声明
错误表明在模块中,它已经包含这样的实例。事实上,它在语义上完全相同,正如我们在源代码中看到的那样:
data Maybe a = Nothing | Just a deriving ( Eq -- ^ @since 2.01 , Ord -- ^ @since 2.01 )
实现Eq
的默认方法是,如果两个项的数据构造函数相同,并且参数在元素方面相等,则返回True
。
您可以定义自己的类型,然后定义实例,例如:
data Maybe2 a = Nothing2 | Just2 a
instance Eq m => Eq (Maybe2 m) where
Just2 x == Just2 y = x == y
Nothing 2== Nothing2 = True
_ == _ = False
作者认为类前面的
(Eq m) => Eq
对于约束实例的内容很重要。这明显不同于在类级别(例如,当你定义Eq时(这样做。然而,我不确定我理解区别是什么。这可能是我不理解OOP中类型类和正则类之间的区别以及前者的"继承"性质的地方。
如果您要使用:
class Eq a => Eq (f a) where
...
那么这意味着Eq (Maybe a)
将暗示Eq a
,所以情况正好相反。然而,这永远不会起作用,因为如果Eq (Maybe Int)
是实例,那么Eq Int
是实例,但是Eq Int
只能是Int ~ f a
的实例(Int
与f a
的类型相同(,并且a
是Eq
的实例。
-
你真的不能"重新定义";CCD_ 16代替CCD_。也就是说,可能有一种方法可以通过
{-#LANGUAGE NoImplicitPrelude #-}
重新定义它,并小心地导入足够多的类型和类型类,以避免导入包含Maybe a
的Eq
定义的文件,或者可能可以使用其中一个杂注来允许重叠声明,但我对此并不熟悉,所以不要相信我的话。 -
Eq m => Eq (Maybe m)
意味着为了使Maybe m
成为Eq
类型类的实例,m
必须已经是Eq
的实例。这是很自然的,因为为了比较Just a
和Just b
,您需要能够比较a
和b
。因此,您无法比较Just (+)
和Just (-)
的相等性。
您可以尝试使用默认从Prelude
导入的另一种方法-使用hiding
。在这种情况下,它可以看起来像这样:
import Prelude hiding (Maybe, Just, Nothing)
data Maybe a = Nothing | Just a
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False