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

Список нескольких параметров, например. 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" (несколько списков параметров с одним параметром).

Ответ 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

Ответ 2

Помимо того, что вы можете писать методы, которые выглядят как часть языка (который вы уже заметили), стоит отметить, что тип inferencer будет работать с одним блоком за раз.

Итак, в этом:

def foo[T](a: T, b: T)(op: (T,T)=>T) = op(a,b)
foo(1,2){_+_}

T сначала будет выведен как Int, который затем будет использоваться как тип двух подчеркиваний в замыкании. Вот как компилятор знает с полной безопасностью типа, что операция + действительна.

Ответ 3

Чтобы ответить на ваш "связанный вопрос", 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>

Ответ 4

Обратные ссылки в аргументах по умолчанию:

case class Foo(bar: Int)

def test(f: Foo)(i: Int = f.bar) = i*i

test(Foo(3))()

Ответ 5

Я знаю, что одним из мотивов является неявный список параметров. "implicit" является свойством списка, а не параметром. Другими были, вероятно, классы case: только первый список параметров стал полем case.