web-dev-qa-db-fra.com

Pourquoi Scala fournit-il à la fois plusieurs listes de paramètres et plusieurs paramètres par liste?

Listes de paramètres multiples, par ex. def foo(a:Int)(b:Int) = {} et plusieurs paramètres par liste, par ex. def foo(a:Int, b:Int) = {} sont sémantiquement équivalents pour autant que je sache, et la plupart des langages fonctionnels n'ont qu'une seule façon de déclarer plusieurs paramètres, par ex. F#.

La seule raison pour laquelle je peux comprendre la prise en charge de ces deux styles de définitions de fonctions est d'autoriser les extensions de langage de type syntaxique en utilisant une liste de paramètres qui ne contient qu'un seul paramètre.

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

peut maintenant être appelé avec la syntaxe

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

Cependant, il pourrait y avoir d'autres façons de soutenir l'utilisation d'accolades sans avoir plusieurs listes de paramètres.

Une question connexe: pourquoi l'utilisation de plusieurs listes de paramètres dans Scala appelée "currying"? Le currying est généralement défini comme une technique pour rendre une fonction n-aire unaire dans le but de supporter une application partielle Cependant, dans Scala on peut appliquer partiellement une fonction sans faire une version "curried" (plusieurs listes de paramètres avec un paramètre chacune) de la fonction.

78
Yuvi Masory

Cela vous permet de faire par exemple:

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
76
Knut Arne Vedaa

En plus de vous permettre d'écrire des méthodes qui ressemblent à une partie du langage (que vous avez déjà repéré), il convient de noter que l'inférenceur de type fonctionnera avec un bloc à la fois.

Donc dans ceci:

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

T sera d'abord déduit comme Int, qui sera ensuite utilisé comme type des deux traits de soulignement dans la fermeture. C'est ainsi que le compilateur sait, en toute sécurité de type, que l'opération + est valide.

45
Kevin Wright

Pour répondre à votre "question connexe", le curry est simplement un moyen de transformer une fonction de plusieurs arguments, par exemple (A, B, C) => D, En une fonction qui prend un argument et renvoie une fonction, par ex. A => (B => (C => D)) (parenthèses affichées mais pas nécessaires).

La forme tuple et la forme au curry sont isomorphes, et nous pouvons traduire librement entre elles. Tous ces éléments sont équivalents, mais ont des implications syntaxiques différentes:

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

Lorsque vous déclarez des groupes de paramètres séparés, c'est le type de curry que vous faites. La méthode multi-paramètre-groupe est une méthode qui renvoie une fonction ... vous pouvez le voir dans le 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>
24
Tom Crockett

Références arrière dans les arguments par défaut:

case class Foo(bar: Int)

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

test(Foo(3))()
21
0__

Je sais que l'une des motivations était des listes de paramètres implicites. "implicite" est une propriété de la liste, pas le paramètre. Une autre était probablement les classes de cas: seule la première liste de paramètres devient des champs de cas.

12
psp