When we define a function literal like,
(a, b) => a < b
then it is basically syntax sugar for:
val lessThan = new Function2[Int, Int, Boolean] {
def apply(a: Int, b: Int) = a < b
}
Does similar thing happen for methods we define with ‘def’ keyword in class/object?
javap knows what’s up.
C:\Users\marti\scala\indy>type test.scala
class Example {
final val lambdaVal = (a: Int, b: Int) => a > b
final def lambdaDef = (a: Int, b: Int) => a > b
final def notALambda(a: Int, b: Int) = a > b
final val etaExpand = notALambda _
}
C:\Users\marti\scala\indy>scalac test.scala -opt:l:inline
C:\Users\marti\scala\indy>"C:\Program Files\Java\jdk1.8.0_191\bin\javap.exe" -p -c Example.class
Compiled from "test.scala"
public class Example {
private final scala.Function2<java.lang.Object, java.lang.Object, java.lang.Object> lambdaVal;
private final scala.Function2<java.lang.Object, java.lang.Object, java.lang.Object> etaExpand;
public final scala.Function2<java.lang.Object, java.lang.Object, java.lang.Object> lambdaVal();
Code:
0: aload_0
1: getfield #21 // Field lambdaVal:Lscala/Function2;
4: areturn
public final scala.Function2<java.lang.Object, java.lang.Object, java.lang.Object> lambdaDef();
Code:
0: invokedynamic #45, 0 // InvokeDynamic #0:apply$mcZII$sp:()Lscala/runtime/java8/JFunction2$mcZII$sp;
5: areturn
public final boolean notALambda(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmple 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
public final scala.Function2<java.lang.Object, java.lang.Object, java.lang.Object> etaExpand();
Code:
0: aload_0
1: getfield #51 // Field etaExpand:Lscala/Function2;
4: areturn
public static final boolean $anonfun$lambdaVal$1(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmple 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
public static final boolean $anonfun$lambdaDef$1(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmple 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
public static final boolean $anonfun$etaExpand$1(Example, int, int);
Code:
0: aload_0
1: iload_1
2: iload_2
3: invokevirtual #57 // Method notALambda:(II)Z
6: ireturn
public Example();
Code:
0: aload_0
1: invokespecial #61 // Method java/lang/Object."<init>":()V
4: aload_0
5: invokedynamic #65, 0 // InvokeDynamic #1:apply$mcZII$sp:()Lscala/runtime/java8/JFunction2$mcZII$sp;
10: putfield #21 // Field lambdaVal:Lscala/Function2;
13: aload_0
14: aload_0
15: invokedynamic #71, 0 // InvokeDynamic #2:apply$mcZII$sp:(LExample;)Lscala/runtime/java8/JFunction2$mcZII$sp;
20: putfield #51 // Field etaExpand:Lscala/Function2;
23: return
private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda);
Code:
0: aload_0
1: invokedynamic #83, 0 // InvokeDynamic #3:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
6: areturn
}
Note that the lambdas use invokeDynamic, called the indylambda strategy in scala. See also: https://gist.github.com/retronym/0178c212e4bacffed568
This is scala 2.12, 2.11 looks more like the desugaring you wrote.
I’m not exactly sure what you mean with “this” happening with “methods we define with def”, but not the difference between two different defs (one that returns a function value: lambdaDef
, one that returns a boolean: notALambda
)
1 Like
curoli
January 9, 2019, 3:03pm
3
If you are asking whether methods defined with def are syntactic sugar for Function objects, the answer is no. def simply defines methods.
But, when you refer to a method where a Function is expected, the compiler may wrap the method into a Function object to fit the expectation. This is called lifting.
__Welcome to Scala 2.12.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.
def getTwice(i: Int): Int = 2*i
getTwice: (i: Int)Int
val twice: Int => Int = getTwice
twice: Int => Int = $$Lambda$1050/1319203143@2eda4eeb
getTwice(3)
res0: Int = 6
twice(3)
res1: Int = 6
twice.apply(3)
res2: Int = 6__
**scala> List(1, 2, 3).map(getTwice)
res3: List[Int] = List(2, 4, 6)
List(1, 2, 3).map(twice)
res4: List[Int] = List(2, 4, 6)**
1 Like