Rather than responding in any direct way, I'll tell you that example
got me wondering once again why I can't write that method the way
which comes to mind first, which is:
def isEmpty: Boolean = {
this foreach (_ => return false)
true
}
Here's (a simulation of) the real isEmpty vs. mine (for the
non-closure part: the real isEmpty creates two additional objects,
mine creates one.)
public static boolean isEmpty1(Trav);
0: new #15; //class scala/runtime/BooleanRef
3: dup
4: iconst_1
5: invokespecial #19; //Method scala/runtime/BooleanRef."<init>":(Z)V
8: astore_1
9: getstatic #25; //Field
scala/util/control/Breaks$.MODULE$:Lscala/util/control/Breaks$;
12: new #27; //class Trav$$anonfun$isEmpty1$1
15: dup
16: aload_0
17: aload_1
18: invokespecial #30; //Method
Trav$$anonfun$isEmpty1$1."<init>":(LTrav;Lscala/runtime/BooleanRef;)V
21: invokevirtual #36; //Method
scala/util/control/Breaks.breakable:(Lscala/Function0;)V
24: aload_1
25: getfield #40; //Field scala/runtime/BooleanRef.elem:Z
28: ireturn
public static boolean isEmpty2(Trav);
0: new #45; //class java/lang/Object
3: dup
4: invokespecial #48; //Method java/lang/Object."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: aload_0
11: new #50; //class Trav$$anonfun$isEmpty2$1
14: dup
15: aload_0
16: aload_1
17: invokespecial #53; //Method
Trav$$anonfun$isEmpty2$1."<init>":(LTrav;Ljava/lang/Object;)V
20: invokeinterface #58, 2; //InterfaceMethod
Trav.foreach:(Lscala/Function1;)V
25: iconst_1
26: istore_2
27: goto 47
30: astore_3
31: aload_3
32: invokevirtual #64; //Method
scala/runtime/NonLocalReturnControl.key:()Ljava/lang/Object;
35: aload_1
36: if_acmpne 49
39: aload_3
40: invokevirtual #67; //Method
scala/runtime/NonLocalReturnControl.value:()Ljava/lang/Object;
43: invokestatic #73; //Method
scala/runtime/BoxesRunTime.unboxToBoolean:(Ljava/lang/Object;)Z
46: istore_2
47: iload_2
48: ireturn
49: aload_3
50: athrow
Hmmm, hey, let's specialize NonLocalReturnControl, and hack hack hack.
Some time later:
public static boolean isEmpty2(Trav);
Code:
Stack=5, Locals=3, Args_size=1
0: new #45; //class java/lang/Object
3: dup
4: invokespecial #48; //Method java/lang/Object."<init>":()V
7: astore_1
8: aload_0
9: new #50; //class Trav$$anonfun$isEmpty2$1
12: dup
13: aload_0
14: aload_1
15: invokespecial #53; //Method
Trav$$anonfun$isEmpty2$1."<init>":(LTrav;Ljava/lang/Object;)V
18: invokeinterface #58, 2; //InterfaceMethod
Trav.foreach:(Lscala/Function1;)V
23: iconst_1
24: goto 40
27: astore_2
28: aload_2
29: invokevirtual #64; //Method
scala/runtime/NonLocalReturnControl.key:()Ljava/lang/Object;
32: aload_1
33: if_acmpne 41
36: aload_2
37: invokevirtual #68; //Method
scala/runtime/NonLocalReturnControl.value$mcZ$sp:()Z <--- look ma,
specialized
40: ireturn
41: aload_2
42: athrow
It's not as if the real version avoids the throw/catch, it just outsources it.
And for the coup de grace, the specialized closure:
public boolean apply$mcZI$sp(int);
0: new #26; //class scala/runtime/NonLocalReturnControl$mcZ$sp
3: dup
4: aload_0
5: getfield #28; //Field nonLocalReturnKey1$1:Ljava/lang/Object;
8: iconst_0
9: invokespecial #32; //Method
scala/runtime/NonLocalReturnControl$mcZ$sp."<init>":(Ljava/lang/Object;Z)V
12: athrow
Of course to get that one I had to write isEmpty like this, which is
safe regardless of what's really in the Traversable although it may
not meet every company's style guidelines. (WE TYPE SAFELY.)
def isEmpty: Boolean = {
this.asInstanceOf[Traversable[Int]] foreach (_ => { return false }: Boolean)
true
}
That's a thing of beauty right there. Maybe not at the source level.
But on the inside.
Also, this "early inlining" idea can be applied to those local methods which are invoked just once ("inlining" them just before lambdalift).