2016-11-11 48 views
4

我想写的不安全Enum功能总版本:安全和多态的枚举哈斯克尔

predMay :: Enum a => a -> Maybe a 
succMay :: Enum a => a -> Maybe a 
toEnumMay :: Enum a => Int -> Maybe a 

我的问题是不安全的功能仅部分如果EnumBounded,所以安全版本应该有不同的形式,在两种情况下:

predMayUnboundedEnum :: Enum a => a -> Maybe a 
predMayUnboundedEnum = Just . pred 

predMayBoundedEnum :: (Enum a, Bounded a) => a -> Maybe a 
predMayBoundedEnum x 
    | fromEnum x == fromEnum (minBound `asTypeOf` x) = Nothing 
    | otherwise = Just (pred x) 

我已经得到我想要的功能,最好的办法是用另一种类型类:

class Enum a => SafeEnum a where 
    predMay :: a -> Maybe a 
instance Enum a => SafeEnum a where 
    predMay = predMayUnboundedEnum 
instance (Enum a, Bounded a) => SafeEnum a where 
    predMay = predMayBoundedEnum 

但这会引起关于Duplicate instance declarations的投诉。

我在这里有没有正确的想法,还是有更好的方法来解决这个问题?有其他人已经完成了吗? (我知道包prelude-safeenum,但Enum对我的主要优点是我们可以deriving它)。这些安全功能甚至可能,或者是野外太多,以至于不能让这样一个简单的解决方案安全?

+2

我建议你写你自己的类,并使用'DefaultSignatures'自动“派生”它(即你会写'实例SafeEnum X'了在'ghc8'中使用'DeriveAnyClass',你甚至可以写'数据X,其中...派生(SafeEnum)',这就等同于上面的例子。你的问题是你想*决定*如果一个类型有一个实例,这根本不可能 - 它违背了类的语义。另外,Enum通常被认为是完全无法执行的,所以你可能会发现你对Enum/Bounded的假设并不总是成立。 – user2407038

回答

5

这是无法完成的。请记住,Haskell类型类别是开放。您的提案需要根据类型是否属于Bounded类来做一些不同的事情。但是你永远不会知道一个类型不是属于这个类。 (好吧,可以知道,例如Integer不能有Bounded实例,但编译不能。没有什么阻止你定义除了普通意义上的废话instance Bounded Integer where {minBound = 7; maxBound = 3}。)

唯一可靠的方式来获得正确的实例是在有界或无界手动排序类型。这是可以做到consisely不够,尤其是带有默认:

class Enum a => SafeEnum a where 
    predMay :: a -> Maybe a 
    predMay = predMayUnboundedEnum 

instance SafeEnum Integer 
instance SafeEnum Rational 
instance SafeEnum Int where predMay = predMayBoundedEnum 
...