Почему 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 называется "каррирование"? Карри обычно определяется как метод создания унарной n-арной функции для поддержки частичного применения. Однако в Scala можно частично применить функцию, не создавая "карри" (несколько списков параметров по одному параметру в каждой) версии функции.
5 ответов
Это позволяет вам сделать, например:
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[T](a: T, b: T)(op: (T,T)=>T) = op(a,b)
foo(1,2){_+_}
T
сначала будет выведено как Int
, который затем будет использоваться как тип двух подчеркиваний в закрытии. Таким образом, компилятор с полной безопасностью типов узнает, что операция + действительна.
Чтобы ответить на ваш "связанный вопрос", карри - это просто способ превратить функцию нескольких аргументов, например (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))()
Я знаю, что одним из мотивов были неявные списки параметров. "неявный" - это свойство списка, а не параметр. Другой, вероятно, был case-классами: только первый список параметров становится полями case.