它们的语义有些不同,但它们的用例大部分是相同的,无论是在实践中还是在代码中的外观。
Currying
从数学意义上柯里化 Scala 中的函数非常简单:
val function = (x: Int, y: Int, z: Int) => 0
// function: (Int, Int, Int) => Int = <function3>
function.curried
// res0: Int => (Int => (Int => Int)) = <function1>
功能与方法
您似乎对 Scala 中的事实感到困惑,(=>
) 功能不等于 (def
) methods。方法不是一流的对象,而函数是(即它有curried
and tupled
方法,以及Function1
有更多的好处)。
然而,可以通过称为 eta 扩展的操作将方法提升为函数。看这个答案了解一些细节。您可以通过编写手动触发它methodName _
,或者如果您为需要函数类型的地方提供一个方法,那么它将隐式完成。
def sumAndAdd4(i: Int, j: Int) = i + j + 4
// sumAndAdd4.curried // <- won't compile
val asFunction = sumAndAdd4 _ // trigger eta expansion
// asFunction: (Int, Int) => Int = <function2>
val asFunction2: (Int, Int) => Int = sumAndAdd4
// asFunction2: (Int, Int) => Int = <function2>
val asFunction3 = sumAndAdd4: (Int, Int) => Int
// asFunction3: (Int, Int) => Int = <function2>
asFunction.curried
// res0: Int => (Int => Int) = <function1>
asFunction2.curried
// res1: Int => (Int => Int) = <function1>
asFunction3.curried
// res2: Int => (Int => Int) = <function1>
{sumAndAdd4 _}.tupled // you can do it inline too
// res3: Int => (Int => Int) = <function1>
多参数列表的eta扩展
正如您可能期望的那样,eta 扩展将每个参数列表提升为自己的函数
def singleArgumentList(x: Int, y: Int) = x + y
def twoArgumentLists(x: Int)(y: Int) = x + y
singleArgumentList _ // (Int, Int) => Int
twoArgumentLists _ // Int => (Int => Int) - curried!
val testSubject = List(1, 2, 3)
testSubject.reduce(singleArgumentList) // Int (6)
testSubject.map(twoArgumentLists) // List[Int => Int]
// testSubject.map(singleArgumentList) // does not compile, map needs Int => A
// testSubject.reduce(twoArgumentLists) // does not compile, reduce needs (Int, Int) => Int
但这并不是数学意义上的柯里化:
def hmm(i: Int, j: Int)(s: String, t: String) = s"$i, $j; $s - $t"
{hmm _} // (Int, Int) => (String, String) => String
在这里,我们得到一个有两个参数的函数,返回另一个有两个参数的函数。
仅指定其部分参数并不那么简单
val function = hmm(5, 6) _ // <- still need that underscore!
与函数一样,您可以毫不费力地返回一个函数:
val alreadyFunction = (i: Int, j: Int) => (k: Int) => i + j + k
val f = alreadyFunction(4, 5) // Int => Int
按照你喜欢的方式去做——Scala 对很多事情都相当不持任何意见。就我个人而言,我更喜欢多个参数列表,因为我通常需要部分应用一个函数,然后将其传递到某个地方,其中剩余的参数将被给出,所以我不需要显式地进行 eta 扩展,并且我在方法定义站点上享受到简洁的语法。