2013-07-14 9 views
5

C'è un question che chiede l'implementazione di lazy val s se sono variabili di classe. Come sono implementati locali variabili, come nelCome vengono implementate le variabili pigri locali di Scala?

def foo[A](a: => A) = { 
    lazy val x: A = a 
    // return something that uses x 
} 
+0

La logica è la stessa, ma non è richiesta alcuna sincronizzazione. –

+0

@RandallSchulz Ma cosa succede se 'x' ne scappa l'ambito, come in [here] (https://github.com/scalaz/scalaz/blob/59bdfecdae88f8e166f8d39cd08879ed87f3db17/core/src/main/scala/scalaz/Name.scala#L39)? Quindi è accessibile da più thread. –

+0

Hmmm ... Sì! In tal caso sarebbe necessaria una sincronizzazione chiara. –

risposta

3

E 'un po' meno efficiente usare vals pigri in un metodo. Il motivo è che in realtà non è possibile nidificare le funzioni in modo che i lazy bit che sono nominalmente allocati nello stack abbiano effettivamente bisogno di andare sullo heap. Quindi devi creare almeno un oggetto extra, e si scopre che Scala ne crea effettivamente due.

class Baz{ 
    def baz(i: => Int, b: Boolean) = { 
    lazy val j = i 
    if (b) j else 0 
    } 
} 

si trasforma in, tra le altre cose

public int baz(scala.Function0, boolean); 
    Code: 
    0: new #12; //class scala/runtime/IntRef 
    3: dup 
    4: iconst_0 
    5: invokespecial #16; //Method scala/runtime/IntRef."<init>":(I)V 
    8: astore_3 
    9: new #18; //class scala/runtime/VolatileByteRef 
    12: dup 
    13: iconst_0 
    14: invokespecial #21; //Method scala/runtime/VolatileByteRef."<init>":(B)V 
    17: astore 4 
    19: iload_2 
    20: ifeq 34 
    23: aload_0 
    24: aload_1 
    25: aload_3 
    26: aload 4 
    28: invokespecial #25; //Method j$1:(Lscala/Function0;Lscala/runtime/IntRef; 
               Lscala/runtime/VolatileByteRef;)I 
    31: goto 35 
    34: iconst_0 
    35: ireturn 

vedere la creazione di IntRef e VolatileByteRef? Questi sono al posto di quelli che normalmente sarebbero solo varselle private per gestire la valigia pigra. E ora j$1, il metodo accessor creato per gestire il recupero e/o la creazione di lazy val, deve prendere le due classi appena create come parametri (oltre alla funzione by-name).

Così, anche se le meccaniche di base sono le stesse, i dettagli di implementazione sono diversi e meno efficienti di se si avesse già un'altra classe in cui attaccare i vars.