2011-02-07 1 views
7

Étant donné:application de fonction partielle fonctionne prématurément codeblock lorsqu'il est utilisé avec underscore

def save(f: => Any)(run:Boolean) { if (run) { println("running f"); f } else println("not running f") } 

Je peux l'appeler avec:

save("test")(true) -> running f 
save("test")(false) -> not running f 
save(throw new RuntimeException("boom!"))(false) -> not running f 
save(throw new RuntimeException("boom!"))(true) -> running f and then exception thrown 

Voici le curieux comportement avec application partielle:

save(throw new RuntimeException("boom!"))(_) -> (Boolean) => Unit = <function1> //as expected 
save(throw new RuntimeException("boom!")) _ -> exception thrown 

Le bloc de code est évalué immédiatement sans être transmis en tant que fonction. Quelle est la différence entre les deux déclarations ci-dessus?

+1

Vous pouvez trouver quelques explications ici: http://stackoverflow.com/questions/2363013/in-scala-why-cant-i-partially-apply-a-function-without-explicitly-specifying-it –

+3

À mon humble avis, c'est un bug. –

Répondre

2

Premier cas,

save(throw new RuntimeException("boom!")) _ 

Selon "Scala Reference" (§6.7), underscore arrière est utilisé à la place de la liste des arguments, et l'expression est converti en

val f: (Boolean) => Unit = save(throw new RuntimeException("boom!")) 

où le premier l'argument de def save est immédiatement évalué.

L'expression e _ est bien formée si e est du type de méthode ou si E est un appel par nom paramètre . Si e est une méthode avec des paramètres, e _ représente et est convertie en un type de fonction par expansion eta (§6.26.5). Si e est une méthode sans paramètre ou un paramètre de type call-by-name de type => T, e _ représente la fonction de type() => T, qui évalue e lorsqu'il est appliqué à la liste de paramètres vide () .

Pour faire les choses fonctionnent comme prévu, quelques modifications sont nécessaires:

scala> def save(f:() => Any)(run:Boolean) { if (run) { println("running f"); f() } else println("not running f") } 
save: (f:() => Any)(run: Boolean)Unit 

scala> val f = save(() => throw new RuntimeException("boom!")) _ 
f: (Boolean) => Unit = <function1> 

scala> f(true) 
running f 
java.lang.RuntimeException: boom! 
     at $anonfun$1.apply(<console>:6) 

Deuxième cas,

save(throw new RuntimeException("boom!"))(_) 

Selon "Scala Reference" (§6.23), lorsque l'espace réservé est utilisé en remplacement d'un argument, l'expression est convertie en

val f: (Boolean) => Unit = save(throw new RuntimeException("boom!"))(_) 
+1

Vraiment, selon la spécification citée, je m'attendrais à appeler par nom de travailler. Après tout, dans ce cas, 'e' n'est évalué que lorsqu'il est appliqué à la liste de paramètres vide. –

+0

Je suis d'accord avec Daniel. Si la spécification indique que l'appel est converti en() => T, la fonction ne sera pas évaluée avant d'être appliquée. – ssanj

0

Le comportement des paramètres call-by-name dans eta expansion est actuellement en cours de révision, voir this bug. Votre code fonctionne comme prévu (c'est-à-dire que la ligne save(throw new RuntimeException("boom!")) _ renvoie une fonction sans lancer l'exception) avec des versions nocturnes récentes de 2.10. Voyons voir s'il reste jusqu'à la libération!

Voir aussi this question pour une question connexe sur le cas général d'expansion eta n'impliquant pas l'appel par le nom.

Questions connexes