我在 Haskell 中遇到以下简单代码的问题:
import Prelude hiding (cycle).
class ICycle a where
cycle :: a -> a
instance ICycle [a] where
cycle [] = []
cycle (x:xs) = xs ++ [x]
instance ICycle Bool where
cycle True = False
cycle False = True
instance Num a => ICycle a where
cycle n = n+1
main = do
print $ cycle $ [1,2,3]
print $ cycle $ True
print $ cycle $ 42
这里前两个实例声明按预期工作,但第三个实例声明根据标志组合触发各种错误。
我知道Num a
不短于ICycle a
,因此编译器无法完成类型检查。在示例中,我发现可以通过将右侧设为更大的术语或首先将感兴趣的类声明为其他类的子类来避免这种情况。相反,在这里,我本质上是想将现有类声明为新类的子类。
我想知道是否有人反对这种类型类的使用。或者,如果有一个自然的解决方案。
最佳答案
对于这个特定的示例,我认为您最好使用 newtype
来包装实例:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Prelude hiding (cycle)
class ICycle a where
cycle :: a -> a
newtype Succ a = Succ { runSucc :: a }
deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)
newtype Pred a = Pred { runPred :: a }
deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)
instance Enum a => ICycle (Succ a) where
cycle = Succ . succ . runSucc
instance Enum a => ICycle (Pred a) where
cycle = Pred . pred . runPred
main = do
print $ cycle $ (42 :: Succ Int)
print $ cycle $ (42 :: Pred Int)
人们可以通过多种方式循环数字 - 通过 succ、通过 pred、通过加倍、通过减半。为实例使用 newtype
的优点
(正如您在问题中指出的那样,使 RHS“更大”)是它让我们拥有所有这些。
标准库使用 Product
执行相同的操作。
和 Sum
对于幺半群
。
换个角度看,如果可以为 Num
定义一个新的父类(super class),为所有实例添加默认实现
Num
,那么您将从所有这些实现中取消该选择。可能以一种没有意义的方式。
关于haskell - 在 Haskell 中声明 "subclass",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29931647/