Object creation for method?


#1

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?


#2

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)


#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)**