Почему 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.

Другие вопросы по тегам