我对Haskell很新,所以我希望这不是一个愚蠢的问题。我有这种数据类型:
data N = I Int | D Double deriving (Show, Eq)
我正在尝试编写一个带有签名(Num a) => (a -> a -> a) -> N -> N -> N
的函数,该函数将函数应用于N
中的数字,并返回带有结果的N.如果N
s都是D
s,则应该只应用该函数并返回D
;如果一个是I
而另一个是D
,它应该将Int
中的I
转换为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'
第二个:
op
我很确定我明白这两个错误的含义;我想第一个是说我的类型签名中的信息不足以让GHC假设Double
返回D
,这是a
值构造函数所需要的,并且第二行是说,因为第一行暗示Double
是a
,所以此行不能使用Int
类型的值,就好像它是(+ 1 2.5 2.5)
一样。我不知道从哪里开始寻找正确的方法来做到这一点。
如果有帮助,我试图让它发挥作用的原因是我跟随Write Yourself a Scheme tutorial;本教程中的所有示例(特别是在Evaluation section中)仅处理整数,但作为练习,我想添加支持整数和浮点数的能力,以便例如6.0
返回(+ 1 2 3)
,6
返回{{1}}。如果我以错误的方式思考这个问题,或者有更简单的方法来实现它,我很乐意听取建议。
答案 0 :(得分:7)
签名
numOp :: (Num a) => (a -> a -> a) -> N -> N -> N
表示numOp
为a -> a -> a
和Num
的每个特定实例提供N
类型的任何单态函数,并从该计算中获取N
。例如,类型
Complex Float -> Complex Float -> Complex Float
或
approxRational :: RealFrac a => a -> a -> Rational
(专门针对a = Rational
)将是合法的第一个论点。
你需要的是一个多态函数,它可以处理所有Num
个实例作为第一个参数,即rank 2类型
numOp :: (forall a. Num a => a -> a -> a) -> N -> N -> N
(您需要RankNTypes
语言扩展名。)