Haskell Constraint不小于实例头

时间:2011-08-26 00:47:31

标签: haskell

有些戒指可以配备标准功能:

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,一切都会变成地狱。

有办法做我想做的事吗?

2 个答案:

答案 0 :(得分:33)

hammar已经提供了解决方案;我想指出这个例子的另一个问题。您要表达的是“每当类型是EqEuclideanDomain的实例时,请使用此规则为Ord创建实例。”但这在Haskell中无法形容。这条线

instance (EuclideanDomain a, Eq a) => Ord a where

实际上意味着“使用此规则为任何类型创建Ord实例。如果EuclideanDomainEq的实例不在范围内,则会出错”。这不好,因为这个规则将与其他所有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。无需延期。