2011-02-07 9 views
7

Dato:applicazione funzione parziale viene eseguito prematuramente codeblock quando viene utilizzato con sottolineatura

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

mi può chiamare 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 

Ecco il comportamento curioso con l'applicazione parziale:

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

Il codeblock viene valutato immediatamente senza essere passato come una funzione. Qual è la differenza tra le 2 affermazioni precedenti?

+1

Puoi trovare alcune spiegazioni qui: http://stackoverflow.com/questions/2363013/in-scala-why-cant-i-partially-apply-a-function-without-explicitly-specifying-it –

+3

IMHO, questo è un bug –

risposta

2

Primo caso,

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

Secondo "Scala Reference" (§6.7), posteriore sottolineatura viene utilizzata al posto della lista degli argomenti, e l'espressione viene convertito

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

dove la prima argomento di def save viene valutato immediatamente.

L'espressione e _ è ben formata se e è di tipo metodo o se E è un parametro chiamata per nome. Se e è un metodo con parametri, e _ rappresenta e convertito in un tipo di funzione dall'espansione eta (§6.26.5). Se e è un metodo senza parametro o parametro per nome di tipo => T, e _ rappresenta la funzione di tipo() => T, che valuta e quando viene applicata all'elenco di parametri vuoto () .

Per rendere le cose funzionano come ci si aspetta, sono necessarie alcune modifiche:

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) 

Secondo caso,

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

Secondo "Scala Reference" (§6.23), quando è segnaposto utilizzato come sostituto di un argomento, l'espressione viene convertita in

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

In realtà, secondo le specifiche citate, mi aspetterei che call by name funzioni. Dopotutto, in questo caso, 'e' viene valutato solo se applicato alla lista dei parametri vuota. –

+0

Sono d'accordo con Daniel. Se la specifica dice che la chiamata viene convertita in() => T, la funzione non verrà valutata fino a quando non viene applicata. – ssanj

0

Il comportamento dei parametri call-by-name in espansione eta è attualmente in fase di revisione, vedere this bug. Il codice funziona come previsto (ovvero, la riga save(throw new RuntimeException("boom!")) _ restituisce una funzione senza generare l'eccezione) con build notturne recenti di 2.10. Vediamo se rimane fino al rilascio!

Vedere anche this question per una domanda correlata sul caso generale di espansione eta che non coinvolge chiamata per nome.