Avoiding boxing/unboxing within function

boxing, macros, scala

Solution

Since you mentioned macros as a possible solution, I got the idea of writing a macro that takes an anonymous function, extracts the apply methods and inserts it into an anonymous class that extends a custom function trait called `F3`. This is the quite long implementation.

The trait `F3`

trait F3[@specialized A, @specialized B, @specialized C, @specialized D] {
  def apply(a:A, b:B, c:C):D
}

The macro

  implicit def function3toF3[A,B,C,D](f:Function3[A,B,C,D]):F3[A,B,C,D] = macro impl[A,B,C,D]

  def impl[A,B,C,D](c:Context)(f:c.Expr[Function3[A,B,C,D]]):c.Expr[F3[A,B,C,D]] = {
    import c.universe._
    var Function(args,body) = f.tree
    args = args.map(c.resetAllAttrs(_).asInstanceOf[ValDef])
    body = c.resetAllAttrs(body)
    val res = 
      Block(
        List(
          ClassDef(
            Modifiers(Flag.FINAL),
            newTypeName("$anon"),
            List(),
            Template(
              List(
                AppliedTypeTree(Ident(c.mirror.staticClass("mcro.F3")),
                  List(
                    Ident(c.mirror.staticClass("scala.Int")),
                    Ident(c.mirror.staticClass("scala.Int")),
                    Ident(c.mirror.staticClass("scala.Double")),
                    Ident(c.mirror.staticClass("scala.Double"))
                  )
                )
              ),
              emptyValDef,
              List(
                DefDef(
                  Modifiers(),
                  nme.CONSTRUCTOR,
                  List(),
                  List(
                    List()
                  ),
                  TypeTree(),
                  Block(
                    List(
                      Apply(
                        Select(Super(This(newTypeName("")), newTypeName("")), newTermName("<init>")),
                        List()
                      )
                    ),
                    Literal(Constant(()))
                  )
                ),
                DefDef(
                  Modifiers(Flag.OVERRIDE),
                  newTermName("apply"),
                  List(),
                  List(args),
                  TypeTree(),
                  body
                )
              )
            )
          )
        ),
        Apply(
          Select(
            New(
              Ident(newTypeName("$anon"))
            ),
            nme.CONSTRUCTOR
          ),
          List()
        )
      )




    c.Expr[F3[A,B,C,D]](res)
  }

Since I defined the macro as implicit, it can be used like this:

def foo(f:F3[Int,Int,Double,Double]) = {
  println(f.apply(1,2,3))
}

foo((a:Int,b:Int,c:Double)=>a+b+c)

Before foo is called, the macro is invoked because `foo` expects an instance of `F3`. As expected, the call to `foo` prints "6.0". Now let's look at the disassembly of the `foo` method, to make sure that no boxing/unboxing takes place:

public void foo(mcro.F3);
  Code:
   Stack=6, Locals=2, Args_size=2
   0:   getstatic       #19; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   3:   aload_1
   4:   iconst_1
   5:   iconst_2
   6:   ldc2_w  #20; //double 3.0d
   9:   invokeinterface #27,  5; //InterfaceMethod mcro/F3.apply$mcIIDD$sp:(IID)D
   14:  invokestatic    #33; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   17:  invokevirtual   #37; //Method scala/Predef$.println:(Ljava/lang/Object;)V
   20:  return

The only boxing that is done here is for the call to `println`. Yay!

One last remark: In its current state, the macro only works for the special case of `Int,Int,Double,Double` but that can easily be fixed. I leave that as an exercise to the reader.

Problem

For a numeric intensive code I have written a function with the following signature: ``` def update( f: (Int,Int,Double) => Double ): Unit = {...} ``` However, because `Function3` is not specialized, every application of `f` results in boxing/unboxing the 3 arguments and the result type. I could use a special updater class: ``` trait Updater { def apply( i: Int, j: Int, x: Double ): Double } def update( f: Updater ): Unit = {...} ``` But the invocation is cumbersome (and java-ish): ``` //with function b.update( (i,j,_) => if( i==0 || j ==0 ) 1.0 else 0.5 ) //with updater b.update( new Updater { def apply( i: Int, j: Int, x: Double ) = if( i==0 || j ==0 ) 1.0 else 0.5 } ) ``` Is there a way to avoid boxing/unboxing while still using the lambda syntax ? I was hoping macros will help, but I cannot figure any solution. EDIT: I analyzed the function3 generated byte code with javap. An unboxed method is generated by the compiler, along the generic method (see below). Is there a way to call the unboxed one directly ? ``` public final double apply(int, int, double); Code: 0: ldc2_w #14; //double 100.0d 3: iload_2 4: i2d 5: dmul 6: iload_1 7: i2d 8: ddiv 9: dreturn public final java.lang.Object apply(java.lang.Object, java.lang.Object, java.lang.Object); Code: 0: aload_0 1: aload_1 2: invokestatic #31; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 5: aload_2 6: invokestatic #31; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I 9: aload_3 10: invokestatic #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D 13: invokevirtual #37; //Method apply:(IID)D 16: invokestatic #41; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double; 19: areturn ```

Original source