Почему currying в Scala нуждается в нескольких списках параметров?

Предположим, что у меня есть функция из 2 параметров, которые мне нужно применить частично, мне нужно определить ее как:

def f(a: Int)(b: Int) = { /* some code */ }

И затем я могу применить его частично как def fWithA = f(a) _

Мой вопрос: для того, чтобы выполнить функцию, почему Scala требует, чтобы параметры были объявлены с использованием нескольких списков параметров? Было бы предпочтительнее иметь возможность выполнять любую функцию по желанию.

Ответ 1

Несколько причин "реального" currying требует нескольких списков параметров в Scala:

  • перегрузка. В отличие от чисто функциональных языков, в Scala вы можете перегружать методы. Если вы частично примените функцию, компилятор может не распознать, какую перегрузку вы имеете в виду. Спецификация ограничивает перегрузку разрешения до первого списка параметров.

  • сообщения об ошибках. "Недостаточно аргументов для вызова метода" - очень полезное (и легко понятное) сообщение об ошибке. Если кто-то разрешил currying для любого метода, сообщение об ошибке было бы "обязательным: но" тип функции типа со многими стрелками ".

  • производительности

    . Запуск на JVM очень эффективен для вызова методов, а функции (через интерфейс) медленнее.

Ответ 2

На самом деле вы можете частично применить любой желаемый метод. Просто вызовите метод и оставьте параметры:

scala> def foo(a: Int, b: Int) = a*b
foo: (a: Int, b: Int)Int

scala> val x = foo(1,_: Int)
x: Int => Int = <function1>

scala> def bar(x: Int, y: Int, z: Int) = x*y+z
bar: (x: Int, y: Int, z: Int)Int

scala> bar(2,_:Int,6)
res0: Int => Int = <function1>

Единственное различие заключается в том, что вы должны сообщить компилятору тип отсутствующего аргумента, поскольку в противном случае он не мог решить между перегруженными методами.

Другим способом, если у вас есть реальная функция, а не метод, было бы вызвать curried для функции:

scala> val f = {(x:Int, y:Int) => x*y}
f: (Int, Int) => Int = <function2>

scala> f.curried
res2: Int => (Int => Int) = <function1>

И вы также можете создать функцию из метода с помощью _:

scala> bar _
res6: (Int, Int, Int) => Int = <function3>

а затем вызовите curried, чтобы:

scala> (bar _).curried
res5: Int => (Int => (Int => Int)) = <function1>