views:

131

answers:

2

Why cannot Scala optimize the following:

a.

implicit def whatever[A](a: A) = new { ... }

to:

b.

class some$generated$name(a: A) {
  ...
}
implicit def whatever[A](a: A) = new some$generated$name(a)

?

Why does it have to use structural typing in this case? I would like Scala compiler to perform this optimization as writing in style b is just too ugly (because, 1. locality of logic is lost, 2. you have to unnecessarily invent names for these additional explicit classes), and a is far less performant than b.

+1  A: 

I think it could, and this can be done with a compiler plugin, to look something like

@extension implicit def whatever[A](a: A) = new { ... }

But I don't know if anyone has written such a plugin yet...

UPDATE:

If I compile this file:

object Main {
  implicit def option[A](a: A) = new { def id = a }  

  def foo(x: String) = x.id
}

and decompile code for foo, reflection is still involved:

F:\MyProgramming\raw>javap -c Main$
Compiled from "Main.scala"
public final class Main$ extends java.lang.Object implements scala.ScalaObject{
public static final Main$ MODULE$;

public static {};
  Code:
   0:   new     #9; //class Main$
   3:   invokespecial   #12; //Method "<init>":()V
   6:   return

public static java.lang.reflect.Method reflMethod$Method1(java.lang.Class);
  Code:
   0:   getstatic       #20; //Field reflPoly$Cache1:Ljava/lang/ref/SoftReference;
   3:   invokevirtual   #27; //Method java/lang/ref/SoftReference.get:()Ljava/lang/Object;
   6:   checkcast       #29; //class scala/runtime/MethodCache
   9:   ifnonnull       29
   12:  new     #23; //class java/lang/ref/SoftReference
   15:  dup
   16:  new     #31; //class scala/runtime/EmptyMethodCache
   19:  dup
   20:  invokespecial   #32; //Method scala/runtime/EmptyMethodCache."<init>":()V
   23:  invokespecial   #35; //Method java/lang/ref/SoftReference."<init>":(Ljava/lang/Object;)V
   26:  putstatic       #20; //Field reflPoly$Cache1:Ljava/lang/ref/SoftReference;
   29:  getstatic       #20; //Field reflPoly$Cache1:Ljava/lang/ref/SoftReference;
   32:  invokevirtual   #27; //Method java/lang/ref/SoftReference.get:()Ljava/lang/Object;
   35:  checkcast       #29; //class scala/runtime/MethodCache
   38:  aload_0
   39:  invokevirtual   #38; //Method scala/runtime/MethodCache.find:(Ljava/lang/Class;)Ljava/lang/r
eflect/Method;
   42:  astore_1
   43:  aload_1
   44:  ifnull  49
   47:  aload_1
   48:  areturn
   49:  aload_0
   50:  ldc     #40; //String id
   52:  getstatic       #42; //Field reflParams$Cache1:[Ljava/lang/Class;
   55:  invokevirtual   #48; //Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class
;)Ljava/lang/reflect/Method;
   58:  astore_1
   59:  new     #23; //class java/lang/ref/SoftReference
   62:  dup
   63:  getstatic       #20; //Field reflPoly$Cache1:Ljava/lang/ref/SoftReference;
   66:  invokevirtual   #27; //Method java/lang/ref/SoftReference.get:()Ljava/lang/Object;
   69:  checkcast       #29; //class scala/runtime/MethodCache
   72:  aload_0
   73:  aload_1
   74:  invokevirtual   #52; //Method scala/runtime/MethodCache.add:(Ljava/lang/Class;Ljava/lang/ref
lect/Method;)Lscala/runtime/MethodCache;
   77:  invokespecial   #35; //Method java/lang/ref/SoftReference."<init>":(Ljava/lang/Object;)V
   80:  putstatic       #20; //Field reflPoly$Cache1:Ljava/lang/ref/SoftReference;
   83:  aload_1
   84:  areturn

public java.lang.Object option(java.lang.Object);
  Code:
   0:   new     #59; //class Main$$anon$1
   3:   dup
   4:   aload_1
   5:   invokespecial   #60; //Method Main$$anon$1."<init>":(Ljava/lang/Object;)V
   8:   areturn

public java.lang.String foo(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokevirtual   #69; //Method option:(Ljava/lang/Object;)Ljava/lang/Object;
   5:   astore_2
   6:   aconst_null
   7:   astore_3
   8:   aload_2
   9:   invokevirtual   #75; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   12:  invokestatic    #77; //Method reflMethod$Method1:(Ljava/lang/Class;)Ljava/lang/reflect/Metho
d;
   15:  aload_2
   16:  iconst_0
   17:  anewarray       #71; //class java/lang/Object
   20:  invokevirtual   #83; //Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang
/Object;)Ljava/lang/Object;
   23:  astore_3
   24:  aload_3
   25:  checkcast       #85; //class java/lang/String
   28:  checkcast       #85; //class java/lang/String
   31:  areturn
   32:  astore  4
   34:  aload   4
   36:  invokevirtual   #91; //Method java/lang/reflect/InvocationTargetException.getCause:()Ljava/l
ang/Throwable;
   39:  athrow
  Exception table:
   from   to  target type
     8    24    32   Class java/lang/reflect/InvocationTargetException


}

Compare with

object Main2 {
  class Whatever[A](a: A) { def id = a }

  implicit def option[A](a: A) = new Whatever(a)

  def foo(x: String) = x.id
}

And decompiling:

F:\MyProgramming\raw>javap -c Main2$
Compiled from "Main2.scala"
public final class Main2$ extends java.lang.Object implements scala.ScalaObject{
public static final Main2$ MODULE$;

public static {};
  Code:
   0:   new     #9; //class Main2$
   3:   invokespecial   #12; //Method "<init>":()V
   6:   return

public Main2$Whatever option(java.lang.Object);
  Code:
   0:   new     #16; //class Main2$Whatever
   3:   dup
   4:   aload_1
   5:   invokespecial   #20; //Method Main2$Whatever."<init>":(Ljava/lang/Object;)V
   8:   areturn

public java.lang.String foo(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokevirtual   #30; //Method option:(Ljava/lang/Object;)LMain2$Whatever;
   5:   invokevirtual   #34; //Method Main2$Whatever.id:()Ljava/lang/Object;
   8:   checkcast       #36; //class java/lang/String
   11:  areturn

}

F:\MyProgramming\raw>javap -c Main2$Whatever
Compiled from "Main2.scala"
public class Main2$Whatever extends java.lang.Object implements scala.ScalaObject{
public java.lang.Object id();
  Code:
   0:   aload_0
   1:   getfield        #14; //Field a:Ljava/lang/Object;
   4:   areturn

public Main2$Whatever(java.lang.Object);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #14; //Field a:Ljava/lang/Object;
   5:   aload_0
   6:   invokespecial   #22; //Method java/lang/Object."<init>":()V
   9:   return

}
Alexey Romanov
bad news! can you comment on why things are this way?
Adam Rabung
That's how structural types normally work (see http://infoscience.epfl.ch/record/138931/files/). This just means that this construction isn't special-cased.
Alexey Romanov
+2  A: 

I've read that too, and have often wanted to ask this same question. But on 2.8, I'm trying it out:

object Main {
  implicit def whatever[A](a: A) = new { def foo = "bar" }  
}

And when I javap it:

public final class Main$$anon$1 extends java.lang.Object{
  public java.lang.String foo();
  public Main$$anon$1();
}

Looks like good news, no?

Adam Rabung
Try the same with `"".foo`. Or more directly, `scala -Xprint:jvm`.
retronym
Bad news: see my edit :(
Alexey Romanov