声明一个具有完整定义的子类

Declare a subclass with a complete definition

我正在尝试执行以下操作:

-- enum which wraps arround
class (Enum a, Bounded a) => CyclicEnum a where
    next, prev :: a -> a

-- for equatable types this is readily implemented
instance (Enum a, Bounded a, Eq a) => CyclicEnum a where
    next x | x == maxBound = minBound
           | otherwise     = succ x

    prev x | x == minBound = maxBound
           | otherwise     = pred x

但是这不会编译,除非我同时启用 FlexibleInstances 和 UndecidableInstances,这似乎不对。做这样的事情的正确方法是什么(即通常使用的方法,最好没有语言扩展)?有吗?

首先,FlexibleInstances 没有任何问题——它只是禁用了标准 Haskell 的一些限制,这些限制主要是出于历史原因,也许是为了使编译器更易于编写。但是如果你只使用 GHC——几乎每个人都这样做——那么就没有真正的理由不使用 FlexibleInstances.

UndecidableInstances 更值得商榷,但在某些情况下也可以使用它。

但是,在您的示例中,我认为根本不需要定义任何新类型 class!为什么不直接定义为自由函数,

next, prev :: (Enum a, Bounded a, Eq a) => a -> a
next x | x == maxBound = minBound
       | otherwise     = succ x
prev x | x == minBound = maxBound
       | otherwise     = pred x

嗯。除了通用实例之外,您的意图可能还为 CyclicEnum 添加其他实例。但这实际上是不可能的!即使 UndecidableInstances。它需要 OverlappingInstances(或者更确切地说,Overlappable 编译指示),但是重叠的实例 确实 不是你应该使用的东西,因为你可以。

您还可以使用 default signatures 来获得您所要求的:仅适用于可等式类型的默认实现(虽然@leftaroundabout 的答案对于这种特定情况可能更好)。

class (Enum a, Bounded a) => CyclicEnum a where
    next, prev :: a -> a
    default next, prev :: Eq a => a -> a

    next x | x == maxBound = minBound
           | otherwise     = succ x

    prev x | x == minBound = maxBound
           | otherwise     = pred x