Ответ 1
Это позволяет вам сделать, например:
scala> def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum
foo: (as: Int*)(bs: Int*)(cs: Int*)Int
scala> foo(1, 2, 3)(4, 5, 6, 7, 9)(10, 11)
res7: Int = 3906
Список нескольких параметров, например. def foo(a:Int)(b:Int) = {}
и несколько параметров в списке, например. def foo(a:Int, b:Int) = {}
являются семантически эквивалентными, насколько я могу судить, и большинство функциональных языков имеют только один способ объявления нескольких параметров, например. F #.
Единственная причина, по которой я могу рассчитывать на поддержку обоих этих стилей определений функций, - это позволить синтаксические языковые расширения, используя список параметров, в котором есть только один параметр.
def withBufferedWriter(file: File)(block: BufferedWriter => Unit)
теперь можно вызвать с помощью синтаксиса
withBufferedWriter(new File("myfile.txt")) { out =>
out write "whatever"
...
}
Однако могут быть и другие способы поддержки использования фигурных скобок без наличия нескольких списков параметров.
Связанный вопрос: почему использование нескольких списков параметров в Scala называется "currying"? Каррирование обычно определяется как метод, позволяющий сделать n-арную функцию унарной для поддержки частичного применения. Однако в Scala можно частично применить функцию, не создавая версию функции "curried" (несколько списков параметров с одним параметром).
Это позволяет вам сделать, например:
scala> def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum
foo: (as: Int*)(bs: Int*)(cs: Int*)Int
scala> foo(1, 2, 3)(4, 5, 6, 7, 9)(10, 11)
res7: Int = 3906
Помимо того, что вы можете писать методы, которые выглядят как часть языка (который вы уже заметили), стоит отметить, что тип inferencer будет работать с одним блоком за раз.
Итак, в этом:
def foo[T](a: T, b: T)(op: (T,T)=>T) = op(a,b)
foo(1,2){_+_}
T
сначала будет выведен как Int
, который затем будет использоваться как тип двух подчеркиваний в замыкании.
Вот как компилятор знает с полной безопасностью типа, что операция + действительна.
Чтобы ответить на ваш "связанный вопрос", currying - это просто способ превратить функцию из нескольких аргументов, например (A, B, C) => D
, в функцию, которая принимает один аргумент и возвращает функцию, например. A => (B => (C => D))
(скобки показаны, но не обязательно).
Корневая форма и карри форма изоморфны, и мы можем свободно переводить между ними. Все они эквивалентны, но имеют разные синтаксические последствия:
(A, B, C, D, E) => F
((A, B), (C, D, E)) => F
(A, B) => (C, D, E) => F
Когда вы объявляете отдельные группы параметров, это то, что вы делаете. Метод с несколькими параметрами - это метод, который возвращает функцию... вы можете видеть это в REPL:
scala> def foo(a:Int, b:Int)(c:Int, d:Int, e:Int):Int = 9
foo: (a: Int,b: Int)(c: Int,d: Int,e: Int)Int
scala> foo _
res4: (Int, Int) => (Int, Int, Int) => Int = <function2>
Обратные ссылки в аргументах по умолчанию:
case class Foo(bar: Int)
def test(f: Foo)(i: Int = f.bar) = i*i
test(Foo(3))()
Я знаю, что одним из мотивов является неявный список параметров. "implicit" является свойством списка, а не параметром. Другими были, вероятно, классы case: только первый список параметров стал полем case.