有些戒指可以配备标准功能:
class (Ring.C a) => EuclideanDomain a where
norm :: a -> Integer
使用此功能,可以以明显的方式订购戒指:
compare x y = compare (norm x) (norm y)
但我不知道如何表明这一点。我试着做
instance (EuclideanDomain a, Eq a) => Ord a where
但这给了我一些警告,当我启用相关的编译器标志时,它告诉我“约束不小于实例头” - 如果我启用UndecidableInstances,一切都会变成地狱。
有办法做我想做的事吗?
答案 0 :(得分:33)
hammar已经提供了解决方案;我想指出这个例子的另一个问题。您要表达的是“每当类型是Eq
和EuclideanDomain
的实例时,请使用此规则为Ord
创建实例。”但这在Haskell中无法形容。这条线
instance (EuclideanDomain a, Eq a) => Ord a where
实际上意味着“使用此规则为任何类型创建Ord
实例。如果EuclideanDomain
和Eq
的实例不在范围内,则会出错”。这不好,因为这个规则将与其他所有Ord实例重叠。
基本上,只要您想编写实例Class typevar
,就需要新类型。
答案 1 :(得分:24)
为了避免无限循环,编译器通常要求实例的约束比实例本身“小”,以便算法终止。您的实例不会使约束中的a
变小,因此这就是编译器所抱怨的。
UndecidableInstances
扩展程序取消了此限制,由您自行决定是否会终止。因此,在使用此扩展时,可以将编译器发送到无限循环。
通常的解决方案是添加newtype
:
newtype ByNorm a = ByNorm a
instance (EuclideanDomain a, Eq a) => Ord (ByNorm a) where
compare (ByNorm x) (ByNorm y) = compare (norm x) (norm y)
现在约束小于实例头,因为它们剥离了newtype
。无需延期。