声明具有完整定义的子类

时间:2015-12-17 15:54:10

标签: haskell typeclass

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

-- 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,否则这不会编译。做这样的事情的正确方法(即,通常使用的方法,最好没有语言扩展)是什么?有吗?

2 个答案:

答案 0 :(得分:5)

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

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

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

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 pragma),但重叠的实例真的不是你应该使用的东西,因为你可以。

答案 1 :(得分:1)

您还可以使用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