2010-10-06 20 views
6

Perché Scala non può ottimizzare quanto segue:Perché non è possibile ottimizzare questo caso di conversioni implicite?

a.

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

a:

b.

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

?

Perché è necessario utilizzare la tipizzazione strutturale in questo caso? Vorrei che il compilatore di Scala eseguisse questa ottimizzazione come scrivere nello stile b è troppo brutto (perché, 1. la località della logica è persa, 2. devi inventare inutilmente nomi per queste classi esplicite aggiuntive) e a è molto meno performante di b.

+0

che non è "tipizzazione strutturale." Quelli sono oggetti wrapper impliciti per i metodi di estensione. La tipizzazione strutturale è quando definisci i tipi in termini di quali metodi o campi hanno, come 'type Closeable = Any {def close: Unit}' –

+1

@MJP: Ricordo di averlo letto da qualche parte in quel codice come in ** a ** usa tipi strutturali. – Jack

+2

@MJP: Sì, e 'new {...}' è definito con precisione in termini di quali metodi e campi ha ... –

risposta

3

penso che potrebbe, e questo può essere fatto con un plugin compilatore, assumendo un aspetto simile

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

Ma non so se qualcuno ha scritto un plugin ancora ...

UPDATE:

Se compilo questo file:

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

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

e decompilare codice per 012.380., la riflessione è ancora coinvolto:

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 


} 

Confronta con

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 
} 

E decompilazione:

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

cattive notizie! puoi commentare perché le cose vanno così? –

+1

Ecco come funzionano normalmente i tipi di struttura (consultare http://infoscience.epfl.ch/record/138931/files/). Questo significa solo che questa costruzione non è di tipo speciale. –

2

Ho letto che anche, e hanno spesso voluto porre questa stessa domanda. Ma su 2,8, sto cercando fuori:

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

E quando ho javap esso:

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

Sembra una buona notizia, no?

Aggiornamento Potete seguire questa ottimizzazione di utilizzare questo trac item

+0

Prova lo stesso con "" ".foo'. O più direttamente, 'scala-Xprint: jvm'. – retronym

+1

Cattive notizie: guarda la mia modifica :( –