Méthodes de liste à paramètres multiples
Pour l'inférence de type
Les méthodes avec plusieurs sections de paramètres peuvent être utilisées pour aider l'inférence de type locale, en utilisant les paramètres de la première section pour inférer des arguments de type qui fourniront un type attendu pour un argument dans la section suivante. foldLeft
dans la bibliothèque standard est l'exemple canonique de ceci.
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
Si c'était écrit comme ça :
def foldLeft[B](z: B, op: (B, A) => B): B
Il faudrait fournir des types plus explicites :
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
Pour une API fluide
Une autre utilisation des méthodes de section à paramètres multiples consiste à créer une API qui ressemble à une construction linguistique. L'appelant peut utiliser des accolades à la place des parenthèses.
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
L'application de N listes d'arguments à une méthode avec M sections de paramètres, où N < M, peut être convertie en une fonction explicitement avec un _
ou implicitement, avec un type attendu de FunctionN[..]
. Il s'agit d'une fonctionnalité de sécurité, voir les notes de modification de Scala 2.0, dans les références Scala, pour en savoir plus.
Fonctions au curry
Les fonctions curées (ou simplement, les fonctions qui renvoient des fonctions) peuvent plus facilement être appliquées à des listes de N arguments.
val f: (a: Int) => (b: Int) => (c: Int) = a + b + c
val g = f(1)(2)
Cette petite commodité en vaut parfois la peine. Notez que les fonctions ne peuvent pas être paramétrées par type, donc dans certains cas une méthode est nécessaire.
Votre deuxième exemple est un hybride : une méthode de section à un paramètre qui renvoie une fonction.
Calculs en plusieurs étapes
Où d'autre les fonctions curry sont-elles utiles ? Voici un modèle qui revient souvent :
def v(t: Double, k: Double): Double = {
// expensive computation based only on t
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);
Comment pouvons-nous partager le résultat f(t)
? Une solution courante consiste à fournir une version vectorisée de la fonction v
:
def v(t: Double, ks: Seq[Double]: Seq[Double] = {
val ft = f(t)
ks map {k => g(ft, k)}
}
C'est moche ! Nous avons enchevêtré des préoccupations sans rapport les unes avec les autres - le calcul g(f(t), k)
et le mappage sur une séquence 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))
Nous pouvons également utiliser une méthode qui renvoie une fonction. Dans ce cas, c'est un peu plus lisible :
def v(t:Double): Double => Double = {
val ft = f(t)
(k: Double) => g(ft, k)
}
Mais si nous essayons de faire la même chose avec une méthode comportant plusieurs sections de paramètres, nous sommes bloqués :
def v(t: Double)(k: Double): Double = {
^
`-- Can't insert computation here!
}