¿Por qué Scala proporciona listas de parámetros múltiples y múltiples parámetros por lista?

Múltiples listas de parámetros, por ejemplo, def foo(a:Int)(b:Int) = {} y múltiples parámetros por lista, p. Ej. def foo(a:Int, b:Int) = {} son semánticamente equivalentes por lo que puedo decir , y la mayoría de los lenguajes funcionales tienen una sola forma de declarar múltiples parámetros, por ejemplo, F #.

La única razón por la que puedo deducir que admite estos dos estilos de definiciones de función es permitir extensiones de lenguaje similares a la syntax utilizando una lista de parámetros que solo tiene un parámetro.

 def withBufferedWriter(file: File)(block: BufferedWriter => Unit) 

ahora se puede llamar con el aspecto sintáctico

 withBufferedWriter(new File("myfile.txt")) { out => out write "whatever" ... } 

Sin embargo, podría haber otras formas de apoyar el uso de llaves sin tener múltiples listas de parámetros.

Una pregunta relacionada: ¿por qué el uso de listas de parámetros múltiples en Scala se llama “currying“? El currying generalmente se define como una técnica para hacer que una función n-ary sea unitaria por el solo hecho de soportar una aplicación parcial. Sin embargo, en Scala se puede aplicar parcialmente una función sin hacer una versión “curried” (listas de parámetros múltiples con un param cada) de la función.

Te permite hacer, por ejemplo:

 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 

Además de permitirle escribir métodos que se ven como parte del lenguaje (que ya vio), vale la pena señalar que el tipo inferencer funcionará con un bloque a la vez.

Entonces en esto:

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

T primero se deduce como Int , que luego se usará como el tipo de los dos guiones bajos en el cierre. Así es como el comstackdor sabe, con seguridad de tipo completo, que la operación + es válida.

Para responder a su “pregunta relacionada”, currying es simplemente una forma de convertir una función de múltiples argumentos, por ejemplo (A, B, C) => D , en una función que toma un argumento y devuelve una función, por ejemplo, A => (B => (C => D)) (se muestran paréntesis pero no son necesarios).

La forma tuple -ized y la forma curried son isomorfas, y podemos traducir libremente entre ellas. Todos estos son equivalentes, pero tienen diferentes implicaciones sintácticas:

 (A, B, C, D, E) => F ((A, B), (C, D, E)) => F (A, B) => (C, D, E) => F 

Cuando declaras grupos de parámetros separados, este es el tipo de currying que estás haciendo. El método de grupo de parámetros múltiples es un método que devuelve una función … puede ver esto en 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 =  

Referencias traseras en argumentos por defecto:

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

Sé que una de las motivaciones fue la lista de parámetros implícitos. “implícito” es una propiedad de la lista, no del parámetro. Otra era probablemente las clases de casos: solo la primera lista de parámetros se convertía en campos de casos.