tuxSlayer
tuxSlayer

Reputation: 2824

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

Upvotes: 3

Views: 103

Answers (1)

Rex Kerr
Rex Kerr

Reputation: 167901

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.

Upvotes: 4

Related Questions