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



我想实现一个简单的RestrictedTypeclass,它有两个函数,lowerBoundupperBound,每个函数都接受一个Restricted实例并返回一个Num

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

我正在尝试创建具有OrthogonalClass数据类型的此类型类的实例。

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,它应该是一个可接受的返回值,因为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匹配并返回NegInf的第二种orthogonalLowerBound模式时,这对我没有帮助,后者是数据类的构造函数Inf是数字的实例。

这就是我的困惑。DoubleNum的实例,那么为什么我不能在我的函数orthogonalLowerBound中返回一个Double呢?为什么我不能返回作为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.做?伟大!现在我可以返回该类型的值a.

此类型签名是对调用该函数的任何人员的承诺,而您(函数的实现者)必须履行该承诺。无论调用方选择哪种类型,都必须返回该类型的值。

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

o :: OrthogonalClass
o = ...
x :: Int
x = orthogonalLowerBound o
y :: Decimal
y = orthogonalLowerBound o

解决方案是什么?如果你的函数应该返回一个Double,只需在其类型签名中这样说:

orthogonalLowerBound :: OrthogonalClass -> Double

当然,在这种情况下,您不能返回Inf,这是应该的:函数不能根据参数的值返回不同的类型。此功能称为"依赖类型",Haskell没有直接支持它。

如果确实需要无穷大,Double类型也包含它,并且有一些方法可以将其作为值获取。例如,查看ieee754包中的infinity

最新更新