在Haskell中,为什么需要在实例声明中指定typeclass?
以下内容无法编译:在Haskell中,为什么需要在实例声明中指定typeclass?,haskell,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 e
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)'
但是,如果我将typeclass添加到实例中,那么它可以工作:
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
必须在typeclassEq
中吗?可以推断a
必须在type classEq
中。这正是它抱怨的原因。您声明了实例Eq(点ab)
,这表示对于任何类型a
和b
,表单点ab
的类型都在Eq
类型类中,但您给出了=
的定义,该定义仅在a
是Eq
的成员时有效
这两件事是不一致的,所以Haskell不会试图猜测哪一件是你真正的意思,它只是将其报告为一个错误。该语言不必以这种方式工作,但它是经过深思熟虑的设计选择。想象一个函数
equals :: a -> a -> Bool
equals = (==)
编译器可以很明显地推断出equals的类型应该是eqa=>a->a->Bool
。但它会出错,因为您声明您的类型适用于所有a
Typeclass实例是类似的,只是我们没有选择让它们以同样的方式被推断,我们可以省略equals
的类型声明。因此,有必要指定约束,就像指定函数的类型签名一样,还必须指定约束
也就是说,ghc不会仅推断约束;它要么推断出整个签名,要么一个也不推断。(好吧,不管是哪种方式,它都可以推断,但是如果你输入了它,那么推断必须比你输入的更一般——而约束也属于这一要求)
把你最初的
实例Eq(点ab)
想象成(==)::(点ab)->(点ab)->Bool
,这是不可能以你喜欢的方式很好地定义的,因为给它主体将有(==)::Eq a=>(点ab)->(点ab)->Bool
我有没有办法不用写Eq a
就写上面的代码?@Eyal没有,除非你希望你的Eq更标准(即使用y记录),在这种情况下你可以Eq@Eyal:不。你为什么要这样做?@Eyal:如果允许的话,这将使确定给定实例的类型类方法的参数所需的约束变得更加困难。如果需要在实例头中放置约束,则只需查看实例定义的第一行。如果您不是必需的,那么您在实例头中的实际类型上几乎是在撒谎,因为类似于点ab
(没有约束)这意味着这适用于Haskell中的所有类型a
和b
。@备选方案:点a b的Eq的派生实例将对a
和b
都有Eq约束,因此您并没有真正避免它。您的Eq
实例声明在我看来很可疑。除了你的代码对编译器所说的之外,它还告诉世界,一个点的第二个组成部分并不重要,也许只是出于某种内部优化的目的。另一方面,你的Show
实例则相反,这两个组件都很重要。如果第二个组件真的不重要,那么你不应该派生Show
,而是应该做一些模糊的事情,比如instance(Show a,Show b)=>Show(a点b)where showsPrec_x(y点)s=“点”++show x
然后,为了进行调试,编写您自己的函数showdirty洗衣房(点AB)=“点”++show a++show b
。但是,如果第二个组件对世界其他地方很重要,那么您应该使用派生(Eq,Show)
,让一切变得更清晰。在某些情况下,使用派生
也可能有效率方面的好处,因此在适当的时候使用它。您编写的typeclass实例没有让它们推断为do函数的选项。为什么不呢?在lambda微积分或代数型系统中有什么东西使它不可能实现吗?还是哈斯凯尔的设计师们干脆把它漏掉了?