Search code examples
scalaclosures

Why scalac generates additional/wrapping closures


First. Consider the following code

scala> val fail = (x: Any) => { throw new RuntimeException }
fail: Any => Nothing = <function1>

scala> List(1).foreach(fail)
java.lang.RuntimeException
    at $anonfun$1.apply(<console>:7)
    at $anonfun$1.apply(<console>:7)
    at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:59)

There is additional anonfun between foreach and exception. One is expected to be a value of fail itself (object of a class Function1[]), but where is the second comes from?

foreach signature takes this function:

def foreach[U](f: A => U): Unit 

So, what is the purpose of the second one?

Second, consider the following code:

scala> def outer() {
     |   def innerFail(x: Any) = { throw new RuntimeException("inner fail") }
     | 
     |   Set(1) foreach innerFail
     | }
outer: ()Unit

scala> outer()
java.lang.RuntimeException: inner fail
    at .innerFail$1(<console>:8)
    at $anonfun$outer$1.apply(<console>:10)
    at $anonfun$outer$1.apply(<console>:10)
    at scala.collection.immutable.Set$Set1.foreach(Set.scala:86)

There are two additional anonfuns... do they really needed? :-E


Solution

  • Let's look at the bytecode.

    object ExtraClosure {
      val fail = (x: Any) => { throw new RuntimeException }
      List(1).foreach(fail)
    }
    

    We find, inside the (single) anonymous function:

    public final scala.runtime.Nothing$ apply(java.lang.Object);
      Code:
       0:   new #15; //class java/lang/RuntimeException
       3:   dup
       4:   invokespecial   #19; //Method java/lang/RuntimeException."<init>":()V
       7:   athrow
    
    public final java.lang.Object apply(java.lang.Object);
      Code:
       0:   aload_0
       1:   aload_1
       2:   invokevirtual   #27; //Method apply:(Ljava/lang/Object;)Lscala/runtime/Nothing$;
       5:   athrow
    

    So it's actually not an extra closure after all. We have one method overloaded with two different return values (which is perfectly okay for the JVM since it treats the type of all parameters as part of the function signature). Function is generic, so it has to take the object return, but the code you wrote returns specifically Nothing, it also creates a method that returns the type you'd expect.

    There are various ways around this, but none are without their flaws. This is the type of thing that JVMs are pretty good at eliding, however, so I wouldn't worry about it too much.

    Edit: And of course in your second example, you used a def, and the anonfun is the class that wraps that def in a function object. That is of course needed since foreach takes a Function1. You have to generate that Function1 somehow.