Question

I have some function:

def f(x: Int) = x * x

and then I call it:

var y = 0
f { y += 1; y }

Bytecode generated for above code looks like:

     0: iconst_0      
     1: istore_1      
     2: aload_0       
     3: iload_1       
     4: iconst_1      
     5: iadd          
     6: istore_1      
     7: iload_1       
     8: invokevirtual #18                 // Method f:(I)I
    11: pop           
    12: return

If I change function def f(x: Int) to represent Call-by-Name:

def f(x: => Int) = x * x

generated bytecode for the same part of code looks like:

     0: new           #24                 // class scala/runtime/IntRef
     3: dup           
     4: iconst_0      
     5: invokespecial #28                 // Method scala/runtime/IntRef."<init>":(I)V
     8: astore_1      
     9: aload_0
     ....

My question is:

Is it a rule that for Call-by-Name we oparate on references or it depends on semantic analysis phase in compilation?

Was it helpful?

Solution

Actual parameters to by-name formal parameters are always packaged into a so-called "thunk" (the terminology goes all the way back to Algol in the 1960s) which makes it possible for there to be zero, one or more (any number) of evaluations of the expression that comprises the actual argument. The bit about multiple evaluations is significant if there are side-effects in that expression.

The specific use of that "reference" has to do with the fact that code that will execute in the called function will have a side-effect on a local variable (literally, specifically a var) in the calling method. That is why the IntRef is involved. A "ref" of one sort or another (depending on the type of the var that gets referenced in the actual parameter expression) will always be used in that circumstance. If no var is involved, the value is simply copied to the thunk.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top