This was a very interesting question to study. There is a saying that you should never rely on the details of the implementation of the code, I think this borders on this.
Try and break what is happening here.
Structural Types:
If you need a structural type, for example, you did this:
def printValues(f: {def apply(x: Int): Int}, from: Int, to: Int): Unit = { println( (from to to).map(f(_)).mkString(" ") ) }
What Scala means is the use of reflection to try to find the apply method at run time and call it dynamically. It transforms into something similar to this:
public static Method reflMethod$Method1(final Class x$1) { MethodCache methodCache1 = Tests$$anonfun$printValues$1.reflPoly$Cache1.get(); if (methodCache1 == null) { methodCache1 = (MethodCache)new EmptyMethodCache(); Tests$$anonfun$printValues$1.reflPoly$Cache1 = new SoftReference((T)methodCache1); } Method method1 = methodCache1.find(x$1); if (method1 != null) { return method1; } method1 = ScalaRunTime$.MODULE$.ensureAccessible(x$1.getMethod("apply", (Class[])Tests$$anonfun$printValues$1.reflParams$Cache1)); Tests$$anonfun$printValues$1.reflPoly$Cache1 = new SoftReference((T)methodCache1.add(x$1, method1)); return method1; }
This is the decompiled Java code that is emitted. In short, he is looking for the apply method.
What happens in Scala <= 2.11
For any version of Scala prior to 2.12, declaring an anonymous function results in a compiler generating a class extending AbstractFunction* , where * is the arity of the function. These abstract function classes in turn inherit Function* and implement their apply method with lambda implementation.
So, for example, if we take your expression:
val fun1 = (x:Int) => x * x
The compiler emits for us:
val fun2: Int => Int = { @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractFunction1$mcII$sp with Serializable { def <init>(): <$anon: Int => Int> = { $anonfun.super.<init>(); () }; final def apply(x: Int): Int = $anonfun.this.apply$mcII$sp(x); <specialized> def apply$mcII$sp(x: Int): Int = x.*(x) }; (new <$anon: Int => Int>(): Int => Int) }; ()
When we look at the bytecode level, we see the generated anonymous class and apply methods:
Compiled from "Tests.scala" public final class othertests.Tests$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable { public static final long serialVersionUID; public final int apply(int); Code: 0: aload_0 1: iload_1 2: invokevirtual #21
Lo and Behold, Scala 2.12
In Scala 2.12, we get something called a SAM transform. SAM types is a Java 8 feature that allows you to shorten the implementation of an interface and instead provide a lambda expression. For instance:
new Thread(() -> System.out.println("Yay in lambda!")).start();
Instead of implementing Runnable and overriding public void run . Scala 2.12 sets the goal of being compatible with SAM types through SAM conversion, whenever possible.
In our particular case, the SAM conversion is possible, which means that instead of Scala Function1[Int, Int] we get a specialized version of scala.runtime.java8.JFunction1$mcII$sp . This JFunction compatible with Java and has the following structure:
package scala.runtime.java8; @FunctionalInterface public interface JFunction1$mcII$sp extends scala.Function1, java.io.Serializable { int apply$mcII$sp(int v1); default Object apply(Object t) { return scala.runtime.BoxesRunTime.boxToInteger(apply$mcII$sp(scala.runtime.BoxesRunTime.unboxToInt(t))); } }
This JFunction1 was specialized (for example, we use @specialized annotation in Scala) to emit a special method for def apply(i: Int): Int . Note one important factor here that this method only implements the apply method of the form Object => Object , not Int => Int . Now we can begin to understand why this can be problematic.
Now, when we compile the same example in Scala 2.12, we see:
def main(args: Array[String]): Unit = { val fun2: Int => Int = { final <artifact> def $anonfun$main(x: Int): Int = x.*(x); ((x: Int) => $anonfun$main(x)) }; ()
We no longer see a method extending AbstractFunction* , we just see a method called $anonfun$main . When we look at the generated bytecode, we see that inside it calls JFunction1$mcII$sp.apply$mcII$sp(int v1); :
public void main(java.lang.String[]); Code: 0: invokedynamic #41, 0
However, if we explicitly extend Function1 ourselves and implement apply , we get a similar behavior with the previous version of Scala, but not exactly the same:
def main(args: Array[String]): Unit = { val anonfun1: Int => Int = { final class $anon extends AnyRef with Int => Int { def <init>(): <$anon: Int => Int> = { $anon.super.<init>(); () }; final def apply(x: Int): Int = x.*(x) }; new $anon() }; { () } }
We no longer extend AbstractFunction* , but we have an apply method that satisfies a condition of a structural type at runtime. At the bytecode level, we see int apply(int) , a object apply(object) and many cases for the @specialization attribute, annotating Function* :
public final int apply(int); Code: 0: aload_0 1: iload_1 2: invokevirtual #183
Output:
We see that the implementation details show how the Scala compiler processes lambda expressions in some circumstances. This is mistake? my feelings tend to no. In any case, the Scala specification guarantees that there must be a method called apply that matches the signature of the lambda, so we call this an implementation detail. Although this is a really interesting quirk, I would not rely on such code in any production environment, as it can be changed.