Haskell 影响 class 解释的实例声明

Haskell instance declaration affecting interpretation of class

除非删除实例声明,否则下面的代码会给我这个错误。此外,我不知道该怎么做 Google,因为错误与似乎导致问题的原因无关?

Test.hs|20 col 31 error| Could not deduce (Integral a) arising from a use of `gendivmod'
|| from the context (Euclidean a)
||   bound by the type signature for
||              gcdBezouts :: Euclidean a => a -> a -> (a, a, a)
||   at /home/jragonfyre/src/haskell/mathlib/Test.hs:17:15-50
|| Possible fix:
||   add (Integral a) to the context of
||     the type signature for
||       gcdBezouts :: Euclidean a => a -> a -> (a, a, a)
|| In the expression: gendivmod x y
|| In a pattern binding: (q, r) = gendivmod x y
|| In the expression:
||   let
||     (q, r) = gendivmod x y
||     (n, m, d) = gcdBezouts y r
||   in (m, n - m * q, d)

这是一个 MWE,用于在我的机器上重现错误。

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

class (Num a, Eq a) => Euclidean a where
  genmod :: a -> a -> a
  genmod a = snd . gendivmod a
  gendiv :: a -> a -> a
  gendiv a = fst . gendivmod a
  gendivmod :: a -> a -> (a,a)
  gendivmod a b = (gendiv a b, genmod a b)
  isUnitEu :: a -> Bool
  isUnitEu = (==1) . abs

instance (Integral a) => Euclidean a where
  gendivmod = divMod

gcdBezouts :: (Euclidean a) => a -> a -> (a, a, a)
gcdBezouts 0 x = (0, 1, x)
gcdBezouts x 0 = (1, 0, x)
gcdBezouts x y = let (q, r) = gendivmod x y
                     (n, m, d) = gcdBezouts y r
                 in (m, n-m*q, d)

至于不使用新类型并使用特定实例声明的动机,我有很多为整数编写的现有代码,我想将它们概括为也适用于多项式,但我没有这样做我不想不得不通过包装和拆开新类型将其重写成一团糟。

如果有其他解决方案可以实现我想要的效果,我将不胜感激。

我不确定你为什么认为你需要一个新类型。

"superclass instance"

instance (Integral a) => Euclidean a where

你可能认为 "every instance of Integral is also an instance of Euclidean" 实际上意味着“ 每种类型 都是 Euclidean 的一个实例,稍后添加约束,即在实例化时我们需要 Integral”。这种情况总是会给你带来麻烦。

我建议的是在您需要的每种类型上显式实例化 Euclidean(这也允许您摆脱 UndecidableInstances)。

instance Euclidean Integer where
    ...

如果你在很多 Integral 类型上实例化它,你应该写 helpers:

integralGenmod :: (Integral a) => a -> a -> a
...

或者,因为你的class有很多方法,从具体化开始:

data EuclideanDomain a = EuclideanDomain a {
    edGenmod :: a -> a -> a,
    ...
}
class Euclidean a where
    euclideanDomain :: EuclideanDomain a

genmod :: (Euclidean a) => a -> a -> a
genmod = edGenmod euclideanDomain    
...

integralEuclidean :: (Integral a) => EuclideanDomain a
integralEuclidean = EuclideanDomain { ... }

instance Euclidean Integral where
    euclideanDomain = integralEuclidean

这使得显式实例化更易于管理,并且还允许您声明结构的更多组合属性。 (参见这个post,具体化一个类型class可以让你走得更远)