2010-10-06 6 views
6

Почему Scala не может оптимизировать следующее:Почему этот случай неявных преобразований не может быть оптимизирован?

a.

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

к:

б.

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

?

Почему в этом случае требуется структурная типизация? Я бы хотел, чтобы компилятор Scala выполнял эту оптимизацию, поскольку запись в стиле b просто слишком уродлива (потому что 1. локальность логики теряется, 2. вам нужно необоснованно придумывать имена для этих дополнительных явных классов) и a намного менее эффективен, чем b.

+0

Это не является «структурным типирование.» Это неявные объекты-оболочки для методов расширения. Структурная типизация - это когда вы определяете типы с точки зрения того, какие методы или поля у них есть, например 'type Closeable = Any {def close: Unit}' – 2010-10-06 02:38:08

+1

@MJP: Я помню, что прочитал его где-то этот код, как в ** ** использует структурные типы. – Jack

+2

@MJP: Да, и 'new {...}' точно определяется с точки зрения того, какие методы и поля он имеет ... –

ответ

3

Я думаю, что мог, и это может быть сделано с помощью плагина компилятора, чтобы выглядеть как

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

Но я не знаю, если кто-то написал такой плагин еще ...

UPDATE:

Если я скомпилировать этот файл:

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

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

и декомпилировать код foo, отражение по-прежнему участвует:

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 


} 

Сравнить с

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 
} 

И декомпиляции:

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 

} 
+0

плохие новости! можете ли вы прокомментировать, почему все так? –

+1

Вот как обычно работают структурные типы (см. Http://infoscience.epfl.ch/record/138931/files/). Это просто означает, что эта конструкция не имеет специальной обложки. –

2

Я читал, что тоже, и часто хотел задать этот же вопрос. Но на 2.8, я пытаюсь это:

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

И когда я javap его:

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

Похоже, хорошие новости, нет?

Update Вы можете отслеживать эту оптимизацию с помощью этого trac item

+0

Попробуйте то же самое с '" ".foo'. Или более непосредственно, 'scala -Xprint: jvm'. – retronym

+1

Плохие новости: см. Мое редактирование :( –