2016-06-23 5 views
1

Quelle est la différence entre ces deux blocs de code?Currie dans Scala: Paramètres multiples dans une fonction incluant une fonction anonyme de type (=> A)

def measure[A](histogram: Histogram)(thunk: ⇒ A): A = { 
    val start = RelativeNanoTimestamp.now 
    try thunk finally { 
    val latency = NanoInterval.since(start).nanos 
    histogram.record(latency) 
} 

def measure[A](histogram: Histogram, thunk: ⇒ A): A = { 
    val start = RelativeNanoTimestamp.now 
    try thunk finally { 
    val latency = NanoInterval.since(start).nanos 
    histogram.record(latency) 
} 

Github Source

+2

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

+0

@jwvh Quel est l'avantage de currying vs l'alternative? Merci d'avoir répondu! – Shehaaz

+0

@jwvh Je vois que vous pouvez appeler partiellement une fonction avec currying. http://docs.scala-lang.org/tutorials/tour/currying.html – Shehaaz

Répondre

3

=> 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 fcurried:

scala> :type f _ 
Int => ((=> Nothing) => Nothing) 

scala> :type f2 _ curried _ 
Int => ((=> Nothing) => Nothing) 
+0

Merci pour votre explication détaillée! Cela fonctionne enfin! https://gist.github.com/shehaaz/7ea265d8b43006d7b395a035c3479925 – Shehaaz