Métodos de lista de parámetros múltiples
Para la inferencia de tipo
Los métodos con múltiples secciones de parámetros se pueden usar para ayudar a la inferencia de tipos locales, usando parámetros en la primera sección para inferir argumentos de tipo que proporcionarán un tipo esperado para un argumento en la sección siguiente. foldLeft
en la biblioteca estándar es el ejemplo canónico de esto.
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
Si esto estuviera escrito como:
def foldLeft[B](z: B, op: (B, A) => B): B
Habría que proporcionar tipos más explícitos:
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
Para API fluida
Otro uso de los métodos de sección de parámetros múltiples es crear una API que parezca una construcción de lenguaje. La persona que llama puede usar llaves en lugar de paréntesis.
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
La aplicación de N listas de argumentos a métodos con M secciones de parámetros, donde N <M, se puede convertir a una función explícitamente con a _
, o implícitamente, con un tipo esperado de FunctionN[..]
. Esta es una característica de seguridad, vea las notas de cambio para Scala 2.0, en las Referencias de Scala, para un fondo.
Funciones al curry
Las funciones curry (o simplemente, las funciones que devuelven funciones) se pueden aplicar más fácilmente a N listas de argumentos.
val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)
Esta pequeña conveniencia a veces vale la pena. Sin embargo, tenga en cuenta que las funciones no pueden ser de tipo paramétrico, por lo que en algunos casos se requiere un método.
Su segundo ejemplo es un híbrido: un método de sección de un parámetro que devuelve una función.
Computación de múltiples etapas
¿Dónde más son útiles las funciones de curry? Aquí hay un patrón que aparece todo el tiempo:
def v(t: Double, k: Double): Double = {
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
¿Cómo podemos compartir el resultado f(t)
? Una solución común es proporcionar una versión vectorizada de v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
¡Feo! Hemos enredado preocupaciones no relacionadas: calcular g(f(t), k)
y mapear una secuencia de ks
.
val v = { (t: Double) =>
val ft = f(t)
(k: Double) => g(ft, k)
}
val t = 1
val ks = Seq(1, 2)
val vs = ks map (v(t))
También podríamos usar un método que devuelva una función. En este caso, es un poco más legible:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
Pero si intentamos hacer lo mismo con un método con múltiples secciones de parámetros, nos quedamos atascados:
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}