有没有办法在haskell中传递未知类型的运算符?

dar*_*rya 3 polymorphism haskell types parametric-polymorphism rank-n-types

我有一个函数f op = (op 1 2, op 1.0 2.0),它需要像这样工作:

f (+)
(3, 3.0)
Run Code Online (Sandbox Code Playgroud)

但是没有声明f它的类型是这样的:

f (+)
(3.0, 3.0)
Run Code Online (Sandbox Code Playgroud)

我正在努力声明f. 它应该采用一个与Num. 在 Haskell 中甚至有可能吗?

rad*_*row 6

问题是您Fractional通过将运算符应用于小数(例如1.0和 )来强制运算符处理类型2.0。您的代码Fractional类型检查因为是 的子类型Num(意味着每个实例Fractional也是 的实例Num)。

以下 GHCi 中的实验应该清楚:

Prelude> :t 0
0 :: Num p => p
Prelude> :t 0.0
0.0 :: Fractional p => p
Prelude> :t 0 + 0.0  -- Fractional taking advantage!
0 + 0.0 :: Fractional a => a
Run Code Online (Sandbox Code Playgroud)

所以,如果你想让它Num完全在s 上工作,你只需要摆脱那些“.0”:

Prelude> f op = (op 1 2, op 1 2)
Prelude> f (+)
(3, 3)
Run Code Online (Sandbox Code Playgroud)

然而

如果您真的需要返回元组的第二个元素所在的行为,而第一个元素Fractional更通用Num,则事情会变得更加复杂。

您传递给的运算符(或实际上是函数)f必须同时显示为具有两种不同的类型。这是通常不可能在普通的Haskell,为每种类型的变量被每个应用固定分配-这意味着(+)需要来决定它是否Float或者Int还是什么。

然而,这是可以改变的。您需要做的是Rank2Types通过:set -XRank2Types在 GHCi 中编写或{-# LANGUAGE Rank2Types #-}.hs文件的最顶部添加来打开扩展。这将允许您以f一种更动态的方式编写其参数类型:

f :: (Num t1, Fractional t2)
  => (forall a. Num a => a -> a -> a) -> (t1, t2)
f op = (op 1 2, op 1.0 2.0)
Run Code Online (Sandbox Code Playgroud)

现在类型检查器不会将任何固定类型分配给op,而是将其保留为多态以进行进一步的专业化。因此,它可以应用于两者1 :: Int1.0 :: Float在相同的上下文中。

的确,

Prelude> f (+)
(3,3.0)
Run Code Online (Sandbox Code Playgroud)

来自评论的提示:可以放宽类型约束以使函数更通用:

f :: (Num t1, Num t2)
  => (forall a. Num a => a -> a -> a) -> (t1, t2)
f op = (op 1 2, op 1 2)
Run Code Online (Sandbox Code Playgroud)

它在所有情况下都能正常工作,以前的版本f会做加上一些snd返回的对的 可能是例如Int

Prelude> f (+)
(3,3)
Prelude> f (+) :: (Int, Float)
(3,3.0)
Run Code Online (Sandbox Code Playgroud)