如果我从 Haskell 示例开始,我希望您不介意,因为 Haskell 是一种比 Scala 简单得多的语言。在 Haskell 中,所有函数都是数学意义上的函数——它们接受一个参数并返回一个值。Haskell 也有元组,你可以编写一个看起来有点像它需要多个参数的函数,作为一个从元组到任何东西的函数。例如:
Prelude> let add = (\(x, y) -> x + y) :: (Int, Int) -> Int
Prelude> add (1, 2)
3
现在我们可以对这个函数进行curryInt -> Int -> Int
来得到一个 type 的函数,而不是(Int, Int) -> Int
:
Prelude> let curriedAdd = curry add
这使我们可以部分应用该功能,例如:
Prelude> let add3 = curriedAdd 3
Prelude> add3 1
4
所以我们有一个很好的柯里化定义——它是一个函数,它接受一个带有元组(特别是一对)的函数作为参数,并返回一个函数,该函数将对中的第一个类型作为参数,并从第二个类型返回一个函数在对中输入原始返回类型。这只是一种罗嗦的说法:
Prelude> :t curry
curry :: ((a, b) -> c) -> a -> b -> c
好的,现在是 Scala。
在 Scala 中,您还可以使用带有元组参数的函数。Scala 还具有接受多个参数(Function2
以及更多参数)的“函数”。这些是(令人困惑的)不同种类的动物。
Scala 也有方法,它们与函数不同(尽管它们可以通过eta 扩展或多或少地自动转换为函数)。方法可以有多个参数,或元组参数,或多个参数列表。
那么在这种情况下说我们正在咖喱什么是什么意思呢?
从字面上看,currying 是你用 a Function2
(和 up) 做的事情:
scala> val add: Function2[Int, Int, Int] = (x: Int, y: Int) => x + y
add: (Int, Int) => Int = <function2>
scala> val curriedAdd = add.curried
curriedAdd: Int => (Int => Int) = <function1>
scala> val add3 = curriedAdd(3)
add3: Int => Int = <function1>
这与我们在 Haskell 案例中看到的情况大致相同,只是我们正在对一个具有多个参数的函数进行柯里化,而这在 Haskell 中并不存在。
现在我很确定这是curry这个词实际出现在 Scala 标准库中的唯一上下文(不包括伴随对象uncurried
上的Function
伴随对象),但是考虑到 Scala 对方法、函数、等等(不要误会我的意思——我喜欢 Scala,但是这部分语言完全是一场灾难),对我来说,在以下上下文中应用这个词似乎也很合理:
def add(x: Int, y: Int) = x + y
def curriedAdd(x: Int)(y: Int) = add(x, y)
在这里,我们将一个接受两个参数的方法变成了一个具有多个参数列表的方法——<em>每个参数列表只接受一个参数(最后一部分很重要)。
事实上,语言规范也在这种情况下使用了该术语,将以下描述为“单一的柯里化函数定义”:
def func(x: Int)
(y: Int) = x + y
(这当然令人困惑,因为这是一个方法,而不是一个函数。)
总结一下:多参数列表是在 Scala 中实现柯里化的一种方法,但并非所有具有多个参数列表的方法都被柯里化——只有每个参数列表都有一个参数的方法。无论如何,所有的术语都很糊涂,所以不要太担心它是否正确。