应用具有Ints或Doubles参数的函数

jcs*_*nt0 6 haskell types

我对Haskell很新,所以我希望这不是一个愚蠢的问题.我有这种数据类型:

data N = I Int | D Double deriving (Show, Eq)
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用签名编写一个函数,该函数(Num a) => (a -> a -> a) -> N -> N -> N将函数应用于Ns中的数字,并返回带有结果的N. 如果Ns都是Ds,它应该只应用函数并返回一个D; 如果一个人是一个I,另一个是D,它应该转换IntIDouble,应用功能,两个DoubleS,并返回一个D; 如果两者都是Is,它应该应用该函数并返回一个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'
Run Code Online (Sandbox Code Playgroud)

不过,我在这两行都有错误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'
Run Code Online (Sandbox Code Playgroud)

第二个:

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'
Run Code Online (Sandbox Code Playgroud)

我很确定我明白这两个错误的含义; 我认为第一个是说我的类型签名中的信息不足以让GHC假设op返回a Double,这是D值构造函数所需要的,而第二个是说,因为第一行暗示的aDouble,这个line不能使用类型的值,a就好像它是一样Int.不过,我不知道从哪里开始寻找正确的方法.

如果它有帮助,我试图让它工作的原因是我正在跟随自己编写一个方案教程 ; 本教程中的所有示例(特别是在评估部分中)仅处理整数,但作为练习,我想添加支持整数和浮点数的能力,例如(+ 1 2.5 2.5)返回6.0(+ 1 2 3)返回6.如果我以错误的方式思考这个问题,或者有更简单的方法来实现它,我很乐意听到建议.

Dan*_*her 7

签名

numOp :: (Num a) => (a -> a -> a) -> N -> N -> N
Run Code Online (Sandbox Code Playgroud)

说对于每个特定的实例和两个s numOp采用任何类型的单态函数,并从中计算出一个.例如,类型的函数a -> a -> aNumNN

Complex Float -> Complex Float -> Complex Float
Run Code Online (Sandbox Code Playgroud)

要么

approxRational :: RealFrac a => a -> a -> Rational
Run Code Online (Sandbox Code Playgroud)

(专门的a = Rational)将是合法的第一个论点.

你需要的是一个多态函数,它可以处理所有Num实例作为第一个参数,即rank 2类型

numOp :: (forall a. Num a => a -> a -> a) -> N -> N -> N
Run Code Online (Sandbox Code Playgroud)

(你需要RankNTypes语言扩展).

  • @ AntalS-Z [不推荐使用`Rank2Types`扩展名](http://www.haskell.org/pipermail/cabal-devel/2012-October/009158.html).我不确定它是否会被完全删除或作为"RankNTypes"的别名保留.虽然它被保留,但它不会仅仅是"<= 2",所以我更喜欢与时俱进,现在到处都使用`RankNTypes`. (3认同)