2011-02-07 20 views
7

dado:aplicación de función parcial antes de tiempo se ejecuta el bloque de código cuando se utiliza con subrayado

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

me puede llamar con:

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 

Aquí está el curioso comportamiento con aplicación parcial:

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

El bloque de código se evalúa inmediatamente sin pasarlo como una función. ¿Cuál es la diferencia entre las 2 afirmaciones anteriores?

+1

Puede encontrar algunas explicaciones aquí: http://stackoverflow.com/questions/2363013/in-scala-why-cant-i-partially-apply-a-function-without-expllicitly-specifying-it –

+3

En mi humilde opinión, esto es un error. –

Respuesta

2

Primer caso,

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

Según "Scala Reference" (§6.7), detrás de subrayado se utiliza en lugar de la lista de argumentos, y la expresión se convierte en

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

donde la primera argumento de def save se evalúa inmediatamente.

La expresión e _ está bien formado si e es de tipo método o si E es un parámetro de llamada por nombre. Si e es un método con parámetros, e _ representa e convertido a un tipo de función mediante la expansión eta (§6.26.5). Si e es un método sin parámetro o call-by-name parámetro de tipo => T, e _ representa la función de tipo() => T, que evalúa e cuando se aplica al lista de parámetros vacía() .

Para hacer las cosas funcionan como se espera, se requieren algunas modificaciones:

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) 

Segundo caso,

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

Según "Scala Reference" (§ 6.23), cuando marcador de posición es utilizado como reemplazo de un argumento, la expresión se convierte en

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

Realmente, de acuerdo con la especificación citada, esperaría que funcione la llamada por nombre. Después de todo, en ese caso 'e' solo se evalúa cuando se aplica a la lista de parámetros vacía. –

+0

Estoy de acuerdo con Daniel. Si como la especificación dice que la llamada se convierte a() => T, la función no se evaluará hasta que se aplique. – ssanj

0

El comportamiento de los parámetros de llamada por nombre en la expansión eta se encuentra actualmente en revisión, consulte this bug. Su código funciona como espera (es decir, la línea save(throw new RuntimeException("boom!")) _ devuelve una función sin lanzar la excepción) con compilaciones nocturnas recientes de 2.10. ¡Veamos si permanece hasta el lanzamiento!

Véase también this question para una pregunta relacionada con el caso general de expansión de eta que no incluye la llamada por nombre.

Cuestiones relacionadas