My other question got closed as a duplicate, so I'll try this again. I have also read this question and what I'm asking is different. I'm interested in learning the internal implementation of how Call-by-Name: => Type
differs from () => Type
.
My confusion is coming from looking at javap and cfr disassembly which shows no difference in the two cases.
e.g. ParamTest.scala:
object ParamTest {
def bar(x: Int, y: => Int) : Int = if (x > 0) y else 10
def baz(x: Int, f: () => Int) : Int = if (x > 0) f() else 20
}
javap output javap ParamTest.scala
:
public final class ParamTest {
public static int baz(int, scala.Function0<java.lang.Object>);
public static int bar(int, scala.Function0<java.lang.Object>);
}
CFR Decompiled output java -jar cfr_0_118.jar ParamTest$.class
:
import scala.Function0;
public final class ParamTest$ {
public static final ParamTest$ MODULE$;
public static {
new ParamTest$();
}
public int bar(int x, Function0<Object> y) {
return x > 0 ? y.apply$mcI$sp() : 10;
}
public int baz(int x, Function0<Object> f) {
return x > 0 ? f.apply$mcI$sp() : 20;
}
private ParamTest$() {
MODULE$ = this;
}
}
EDIT 1:
Scala Syntax Tree: scalac -Xprint:parse ParamTest.scala
package <empty> {
object ParamTest extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def bar(x: Int, y: _root_.scala.<byname>[Int]): Int = if (x.$greater(0))
y
else
10;
def baz(x: Int, f: _root_.scala.Function0[Int]): Int = if (x.$greater(0))
f()
else
20
}
}
EDIT 2: Mailing list research:
Read this interesting post on the mailing list which essentially states that => T
is implemented as () => T
. Quote:
First, look at
f: => Boolean
Although this is called a "by-name parameter", it is actually implemented as a
Function0
,
f: () => Boolean
just with different syntax used on both ends.
Now I'm even more confused by this answer which explicitly states that the two are different.
Questions:
bar
from baz
? The method signatures (not implementation) for both are identical in the decompiled code.bar
has the second argument of type _root_.scala.<byname>[Int]
. What does it do? Any explanation, pointers in scala source or equivalent pseudo code will be helpful.=> T
a special subclass of Function0
?How is Scala distinguishing
bar
frombaz
? The method signatures (not implementation) for both are identical in the decompiled code.
Scala doesn't need to distinguish between the two. From it's perspective, these are two different methods. What's interesting (to me at least) is that if we rename baz
into bar
and try to create an overload with a "call-by-name" parameter, we get:
Error:(12, 7) double definition:
method bar:(x: Int, f: () => Int)Int and
method bar:(x: Int, y: => Int)Int at line 10
have same type after erasure: (x: Int, f: Function0)Int
def bar(x: Int, f: () => Int): Int = if (x > 0) f() else 20
Which is a hint for us that under the covers, something is going on with the translation to Function0
.
Does the difference in the two scenarios not get persisted into the compiled bytecode?
Before Scala emits JVM bytecode, it has additional phases of compilation. An interesting one in this case is to look at the "uncurry" stage (-Xprint:uncurry
):
[[syntax trees at end of uncurry]]
package testing {
object ParamTest extends Object {
def <init>(): testing.ParamTest.type = {
ParamTest.super.<init>();
()
};
def bar(x: Int, y: () => Int): Int = if (x.>(0))
y.apply()
else
10;
def baz(x: Int, f: () => Int): Int = if (x.>(0))
f.apply()
else
20
}
}
Even before we emit byte code, bar
is translated into a Function0
.
Is the decompiled code inaccurate
No, it's definitely accurate.
I found that the scalac syntax tree does show a difference, bar has the second argument of type root.scala.[Int]. What does it do?
Scala compilation is done in phases, where each phases output is the input to the next. In addition to the parsed AST, Scala phases also create symbols, such that if one stage relys on a particular implementation detail it will have it available. <byname>
is a compiler symbol which shows that this method uses "call-by-name", so that one of the phases can see that and do something about it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With