=> A
est un paramètre paresseux. Il sera évalué lorsqu'il est fait référence à la fonction. Cela peut être une fonction produisant une valeur, ou juste une valeur.
La principale différence entre les listes de paramètres uniques et multiples comme dans votre exemple:
def measure[A](histogram: Histogram)(thunk: ⇒ A)
def measure[A](histogram: Histogram, thunk: ⇒ A)
(sans tenir compte implicits et l'inférence de type) est de savoir comment vous appliquez une fonction:
scala> def f[A](i: Int)(p: => A): A = { p }
f: [A](i: Int)(p: => A)A
scala> f(1)(2)
res0: Int = 2
scala> f(1){ println("something") }
something
scala> f(1){
| println("test")
| }
test
scala> def f2[A](i: Int, p: => A): A = { p }
f2: [A](i: Int, p: => A)A
scala> f2(1, 2)
res4: Int = 2
scala> f2(1, println("test"))
test
scala> f2(1, { println("test") })
test
Voir que f
avec plusieurs listes de paramètres nous permet d'écrire dans ce style: f(...){...}
, tandis que f2
est un peu moins élégant si vous avez un bloc de code multiligne comme deuxième argument: f(..., {...})
.
En outre, si vous faites beaucoup de de taitement/application partielle alors f2
est un peu plus facile à traiter que f
:
scala> val f_withFirstArg = f(1) _
f_withFirstArg: (=> Nothing) => Nothing = <function1>
scala> val f2_withFirstArg = f2(1, _)
<console>:8: error: missing parameter type for expanded function ((x$1) => f2(1, x$1))
val f2_withFirstArg = f2(1, _)
^
Nous devons spécifier type de paramètre explicitement, l'inférence de type échoue court:
scala> val f2_withFirstArg = f2(1, _: String)
f2_withFirstArg: String => String = <function1>
Si vous voulez vous faire une idée à ce sujet, il est intéressant de souligner qu'ils sont d'un type différent:
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _
(Int, => Nothing) => Nothing
f
est une fonction qui prend un Int
et renvoie une autre fonction qui prend le type A
et produira le type A
. f2
est une fonction qui prend 2 arguments: Int
et A
et renvoie A
.
Cela dépend vraiment de votre code. Si vous avez besoin de faire beaucoup d'applications partielles ou si vous avez besoin de moins d'annotations en raison des imperfections d'inférence de type, utilisez plusieurs listes de paramètres. Dans le cas contraire, il n'est pas nécessaire de trop compliquer les choses et d'utiliser des fonctions régulières de liste de paramètres uniques.
Enfin, vous pouvez toujours convertir d'un type de fonction à une autre aussi longtemps qu'il est logique:
scala> f2 _
res13: (Int, => Nothing) => Nothing = <function2>
scala> f2 _ curried
warning: there were 1 feature warning(s); re-run with -feature for details
res14: Int => ((=> Nothing) => Nothing) = <function1>
scala> f _ curried
<console>:9: error: value curried is not a member of Int => ((=> Nothing) => Nothing)
f _ curried
^
scala> f _ tupled
<console>:9: error: value tupled is not a member of Int => ((=> Nothing) => Nothing)
f _ tupled
^
scala> f2 _ tupled
warning: there were 1 feature warning(s); re-run with -feature for details
res17: ((Int, => Nothing)) => Nothing = <function1>
Notez que nous ne pouvons pas faire f
cari parce qu'il est déjà. Nous ne pouvons pas faire f
tupled car cela ne changerait rien. Cependant, nous pouvons convertir f2
en utilisant f
curried
:
scala> :type f _
Int => ((=> Nothing) => Nothing)
scala> :type f2 _ curried _
Int => ((=> Nothing) => Nothing)
Au 1er, les deux arguments sont cari, dans le 2ème ils ne sont pas. Qu'est-ce que vous essayez d'apprendre/résoudre? – jwvh
@jwvh Quel est l'avantage de currying vs l'alternative? Merci d'avoir répondu! – Shehaaz
@jwvh Je vois que vous pouvez appeler partiellement une fonction avec currying. http://docs.scala-lang.org/tutorials/tour/currying.html – Shehaaz