再会。我是Haskell的新手。对于声明和实例化一些自定义类,我不清楚。

  • haskell中有一个标准类Integral。根据骇客行为,Integral声明了强制方法quot :: a -> a -> a。因此,这意味着该类的每个实例都应具有此方法实现,对吗?
  • 我们可以使用Integral作为参数来声明一些函数,例如:

  • proba :: (Integral a) => a -> a -> a
    proba x y = x `quot` y
    

    到现在为止还挺好
  • 现在让我们声明我们自己的类Proba:

  • 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
    

    但是我不想。 我想要每个积分一个实例。 但是,当我尝试执行此操作时,出现错误:

    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都应有效,不是吗?

    也许有一种方法可以达到同样的效果?

    最佳答案

    如错误消息所示,您可以使用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

    10-08 12:41