美好的一天。我是Haskell的新手。关于声明和实例化一些自定义类,有一件事我不清楚。
haskell中有一个标准类Integral
。根据hackage,Integral
声明了强制方法quot :: a -> a -> a
。所以这意味着该类的每个实例都应该有这个方法实现,对吧?
我们可以使用Integral作为参数来声明一些函数,例如:
proba :: (Integral a) => a -> a -> a
proba x y = x `quot` y
到目前为止一切顺利
class Proba a where
proba :: a -> a -> a
我可以实现这样的Int或Integer(或其他数据类型)实例:
instance Proba Integer where
proba x y = x `quot` y
instance Proba Int where
proba x y = x `quot` y
但我不想。 我希望每个Integral都有一个实例。但是当我尝试这样做时,我收到一个错误:
instance (Integral a) => Proba a where
proba x y = x `quot` y
Illegal instance declaration for `Proba a'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
In the instance declaration for `Proba a'
好吧,它似乎要求我提供不同的类型变量而不是类。但为什么?!为什么仅仅在这里Integral
就足够了?由于每个quot
都声明了Integral
,因此该实例对每个Integral
都有效,请对其进行调整吗?
也许有办法达到同样的效果?
答案 0 :(得分:6)
正如错误消息所示,您可以使用FlexibleInstances
(一种相当常见且安全的扩展名)来允许此行为,但您还需要UndecidableInstances
:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
class Proba a where
proba :: a -> a -> a
instance Integral a => Proba a where
proba = quot
默认启用此功能的原因是因为它特别是GHC扩展,并且它不是Haskell98规范的一部分。您会发现有很多语言扩展非常有用且使用安全,而且通常只需要在特定模块中启用它们。而不只是问为什么不是这个默认",还要问"我什么时候不希望这是默认的?"。
实现此扩展的另一种方法是将类型类直接编码为数据类型:
data Proba a = Proba
{ proba :: a -> a -> a
}
integralProba :: Integral a => Proba a
integralProba = Proba quot
然后你可以传递它
foldProba :: Proba a -> a -> [a] -> a
foldProba p = foldr (proba p)
然后,如果您有foldProba integralProba
,则会自动将类型约束为Integral a => a -> [a] -> a
。