Métodos de lista de parâmetros múltiplos
Para inferência de tipo
Métodos com várias seções de parâmetro podem ser usados para auxiliar a inferência de tipo local, usando parâmetros na primeira seção para inferir argumentos de tipo que fornecerão um tipo esperado para um argumento na seção subsequente. foldLeft
na biblioteca padrão é o exemplo canônico disso.
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
Se fosse escrito como:
def foldLeft[B](z: B, op: (B, A) => B): B
Seria necessário fornecer tipos mais explícitos:
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
Para API fluente
Outro uso para métodos de seção de vários parâmetros é criar uma API que se pareça com uma construção de linguagem. O chamador pode usar colchetes em vez de parênteses.
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
A aplicação de N listas de argumentos a métodos com M seções de parâmetros, onde N <M, podem ser convertidos em uma função explicitamente com a _
, ou implicitamente, com um tipo esperado de FunctionN[..]
. Este é um recurso de segurança, consulte as notas de mudança para Scala 2.0, nas Referências do Scala, para um histórico.
Funções Curried
Funções curried (ou simplesmente funções que retornam funções) podem ser aplicadas mais facilmente a N listas de argumentos.
val f = (a: Int) => (b: Int) => (c: Int) => a + b + c
val g = f(1)(2)
Esta pequena conveniência às vezes vale a pena. Observe que as funções não podem ser do tipo paramétricas, portanto, em alguns casos, um método é necessário.
Seu segundo exemplo é um híbrido: um método de seção de um parâmetro que retorna uma função.
Computação Multi Stage
Onde mais as funções curried são úteis? Aqui está um padrão que surge o tempo todo:
def v(t: Double, k: Double): Double = {
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
Como podemos compartilhar o resultado f(t)
? Uma solução comum é fornecer uma versão vetorial de v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
Feio! Envolvemos questões não relacionadas - calcular g(f(t), k)
e mapear uma sequência 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))
Também podemos usar um método que retorna uma função. Neste caso, é um pouco mais legível:
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
Mas se tentarmos fazer o mesmo com um método com várias seções de parâmetros, ficaremos presos:
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
val f: (a: Int) => (b: Int) => (c: Int) = a + b + c
Você pode curry apenas funções, não métodos.
add
é um método, portanto, você precisa de_
para forçar sua conversão em uma função.add2
retorna uma função, portanto,_
não é apenas desnecessário, mas não faz sentido aqui.Considerando como diferentes métodos e funções são (por exemplo, a partir da perspectiva da JVM), Scala faz um bom trabalho muito borrar a linha entre eles e fazendo "a coisa certa" na maioria dos casos, mas não é uma diferença, e às vezes você só precisa para saber sobre isso.
fonte
Acho que ajuda a compreender as diferenças se eu adicionar que com
def add(a: Int)(b: Int): Int
você basicamente apenas definir um método com dois parâmetros, apenas esses dois parâmetros são agrupados em duas listas de parâmetros (veja as consequências disso em outros comentários). Na verdade, esse método é apenas noint add(int a, int a)
que diz respeito a Java (não Scala!). Quando você escreveadd(5)_
, isso é apenas um literal de função, uma forma mais curta de{ b: Int => add(1)(b) }
. Por outro lado, comadd2(a: Int) = { b: Int => a + b }
você define um método que tem apenas um parâmetro, e para Java seráscala.Function add2(int a)
. Quando você escreveadd2(1)
em Scala, é apenas uma chamada de método simples (em oposição a um literal de função).Observe também que
add
tem (potencialmente) menos sobrecarga do queadd2
se você fornecer todos os parâmetros imediatamente. Comoadd(5)(6)
apenas traduzidoadd(5, 6)
no nível da JVM, nenhumFunction
objeto é criado. Por outro lado,add2(5)(6)
primeiro criará umFunction
objeto que o encerra5
e, em seguida, o chamaráapply(6)
.fonte