我正在尝试执行以下操作:
-- 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,否则这不会编译。做这样的事情的正确方法(即,通常使用的方法,最好没有语言扩展)是什么?有吗?
答案 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