我对Haskell很陌生,所以我希望这不是一个愚蠢的问题。我的数据类型是:
data N = I Int | D Double deriving (Show, Eq)
我试图写一个签名(Num a) => (a -> a -> a) -> N -> N -> N
的函数,它将函数应用于N
s内的数字,并返回结果的N。如果N
s都是D
s,则只需应用该函数并返回D
;如果一个是I
,另一个是D
,它应该将I
中的Int
转换为Double
,将该函数应用于两个Double
,并返回D
;如果两者都是I
,则应用该函数并返回I
。这是我目前为止的(破碎的)代码:
widen :: N -> N -> (N, N)
widen (I i) d@(D _) = (D (fromIntegral i), d)
widen d@(D _) i@(I _) = widen i d
widen x y = (x, y)
numOp :: (Num a) => (a -> a -> a) -> N -> N -> N
numOp op x y = case widen x y of (D x', D y') -> D $ x' `op` y'
(I x', I y') -> I $ x' `op` y'
我得到numOp
的两行错误,虽然。第一个是:
Could not deduce (a ~ Double)
from the context (Num a)
bound by the type signature for
numOp :: Num a => (a -> a -> a) -> N -> N -> N
at <line num>
In the second argument of `($)', namely x' `op` y'
In the expression: D $ x' `op` y'
In a case alternative: (D x', D y') -> D $ x' `op` y'
第二个:
Couldn't match type `Double' with `Int'
Expected type: Int
Actual type: a
In the second argument of `($), namely x' `op` y'
In the expression: I $ x' `op` y'
In a case alternative: (I x', I y') -> I $ x' `op` y'
我很确定我理解这两个错误的意思;我认为第一个是说我的类型签名中的信息不足以让GHC假设op
返回Double
,这是D
值构造函数所要求的,第二个是说,因为第一行意味着a
是Double
,这一行不能使用a
类型的值,好像它是Int
。但是,我不知道从哪里开始寻找正确的方法来做到这一点。
如果它有帮助,我试图让这个工作的原因是,我跟着写自己的方案教程;本教程中的所有示例(特别是在评估部分)都只处理整数,但作为练习,我想添加支持整数和浮点数的能力,例如(+ 1 2.5 2.5)
返回6.0
, (+ 1 2 3)
返回6
。如果我想错了,或者有更简单的方法来完成它,我很乐意听到建议。
签名
numOp :: (Num a) => (a -> a -> a) -> N -> N -> N
表示numOp
对Num
和两个N
的每个特定实例取a -> a -> a
类型的任意单态函数,并从中计算一个N
。例如,一个类型为
Complex Float -> Complex Float -> Complex Float
或
approxRational :: RealFrac a => a -> a -> Rational
(专用于a = Rational
)将是合法的第一个参数。
你需要的是一个多态函数,可以处理所有Num
实例作为第一个参数,即排名2类型
numOp :: (forall a. Num a => a -> a -> a) -> N -> N -> N
(您需要RankNTypes
语言扩展)