如何在以下非常简单的函数中解决此 class 实例错误?

How can I resolve this class instance error in the following very simple function?

我想实现一个简单的 Restricted 类型 class,它有两个函数,lowerBoundupperBound,每个函数都有一个 Restricted实例和 return 一个 Num.

class Restricted r where
    lowerBound :: (Num n) => r -> n
    upperBound :: (Num n) => r -> n

我正在尝试使用 OrthogonalClass 数据类型创建此类型class 的实例。

instance Restricted OrthogonalClass where
    lowerBound p = orthogonalLowerBound p
    upperBound p = orthogonalUpperBound p

哪里

orthogonalLowerBound :: (Num a) => OrthogonalClass -> a
orthogonalLowerBound Legendre = a
    where a = (-1.0) :: Double
-- orthogonalLowerBound Hermite = NegInf

(-1.0) 是一个 Double,它应该是一个可接受的 return 值,因为 DoubleNum 的一个实例。但是,我从编译器中收到以下错误:

Polynomials.hs:20:33: error:
    • Couldn't match expected type ‘a’ with actual type ‘Double’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          orthogonalLowerBound :: forall a. Num a => OrthogonalClass -> a
        at Polynomials.hs:19:1-55
    • In the expression: a
      In an equation for ‘orthogonalLowerBound’:
          orthogonalLowerBound Legendre
            = a
            where
                a = (- 1.0) :: Double
    • Relevant bindings include
        orthogonalLowerBound :: OrthogonalClass -> a
          (bound at Polynomials.hs:20:1)
   |
20 | orthogonalLowerBound Legendre = a
   |                                 ^

我可以通过将 (-1.0) 更改为 (-1) 来解决这个问题。但是,当我取消注释匹配 Hermite 和 returns NegInf 的第二个模式时,这对我没有帮助,它是数据 [=57 的构造函数=] Inf 这是一个数字的实例。

这是我的困惑。 A DoubleNum 的一个实例,那么为什么我不能在我的函数 orthogonalLowerBound 中 return a Double?为什么我不能 return 作为 Num 实例的 Inf

我错过了什么?

这是 OrthogonalClassInf 的完整定义,以备您需要更多信息

data OrthogonalClass = Legendre | Laguerre | Hermite | Tchebychev
-- Playing around with infinity
data Inf = NegInf | PosInf | Undef | Finite deriving (Show)

instance Num Inf where
    (+) a b = infAdd a b
    (-) a b = infSub a b
    (*) a b = infMult a b
    signum a = infSignum a
    abs a = infAbs a
    fromInteger a = infFromInt a

infAdd :: Inf -> Inf -> Inf
infAdd NegInf NegInf = NegInf
infAdd PosInf PosInf = PosInf
infAdd PosInf NegInf = Undef
infAdd NegInf PosInf = Undef
infAdd Undef _ = Undef
infAdd _ Undef = Undef
infAdd NegInf Finite = NegInf
infAdd PosInf Finite = PosInf
infAdd Finite Finite = Finite

infSub :: Inf -> Inf -> Inf
infSub NegInf NegInf = Undef
infSub PosInf PosInf = Undef
infSub PosInf NegInf = PosInf
infSub NegInf PosInf = NegInf
infSub Undef _ = Undef
infSub _ Undef = Undef
infSub NegInf Finite = NegInf
infSub PosInf Finite = PosInf
infSub Finite Finite = Finite

infMult :: Inf -> Inf -> Inf
infMult NegInf NegInf = PosInf
infMult PosInf PosInf = PosInf
infMult PosInf NegInf = NegInf
infMult NegInf PosInf = NegInf
infMult Undef _ = Undef
infMult _ Undef = Undef
infMult NegInf Finite = NegInf
infMult PosInf Finite = PosInf
infMult Finite Finite = Finite

infAbs :: Inf -> Inf
infAbs NegInf = PosInf
infAbs PosInf = PosInf
infAbs Undef  = Undef
infAbs Finite = Finite

infSignum :: (Num a) => Inf -> a
infSignum NegInf = (-1)
infSignum PosInf = 1
infSignum Undef = 0
infSignum Finite = 0

infFromInt :: (Integral i) => i -> Inf
infFromInt x = Finite

这是对泛型类型方向的一种非常常见的混淆。

简而言之:选择泛型的是函数的调用者,而不是实现者

如果您有一个具有此签名的函数:

orthogonalLowerBound :: (Num a) => OrthogonalClass -> a

那个签名说:嘿你,谁调用我的函数!选择一个类型。任何类型。我们称它为 a。现在确保有一个实例 Num a。完毕?伟大的!现在我可以 return 你一个 a.

类型的值

此类型签名是对函数调用者的承诺,而您作为函数的实现者必须履行该承诺。无论调用者选择什么类型,您都必须 return 该类型的值。

并且调用者可以选择任何具有 Num 实例的类型,例如:

o :: OrthogonalClass
o = ...

x :: Int
x = orthogonalLowerBound o

y :: Decimal
y = orthogonalLowerBound o

解决办法?如果你的函数应该 return a Double,只需在它的类型签名中这样说:

orthogonalLowerBound :: OrthogonalClass -> Double

当然,在这种情况下你不能 return Inf,这是应该的:一个函数不能 return 不同的类型,这取决于参数的值。此功能称为“依赖类型”,Haskell 没有直接支持它。

如果您确实需要无穷大,Double 类型也包含它,并且有多种方法可以将其作为值获取。例如,看看 infinity from the ieee754 package