2010-10-06 9 views
6

Pourquoi Scala ne peut-il pas optimiser les éléments suivants:Pourquoi ce cas de conversions implicites ne peut-il pas être optimisé?

a.

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

à:

b.

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

? Pourquoi faut-il utiliser un typage structurel dans ce cas? Je voudrais que Scala compilateur pour effectuer cette optimisation comme l'écriture dans le style b est tout simplement trop moche (parce que, 1. localité de la logique est perdue, 2. vous devez inutilement inventer des noms pour ces classes explicites supplémentaires), et un est beaucoup moins performant que b.

+0

Ce n'est pas « typage structurel. » Ce sont des objets wrapper implicites pour les méthodes d'extension. Le typage structurel est lorsque vous définissez des types en termes de méthodes ou champs qu'ils ont, tels que 'type Closeable = Any {def close: Unit}' –

+1

@MJP: Je me souviens de l'avoir lu quelque part que le code comme ** a ** utilise des types structurels. – Jack

+2

@MJP: Oui, et 'new {...}' est défini avec précision en termes de méthodes et de champs ... –

Répondre

3

Je pense qu'il pourrait, et cela peut être fait avec un plug-in de compilateur, de regarder quelque chose comme

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

Mais je ne sais pas si quelqu'un a encore écrit un plug-in ...

MISE à JOUR:

Si je compile ce fichier:

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

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

et décompiler code foo, la réflexion est toujours impliqué:

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 


} 

Comparer avec

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 
} 

Et décompilation:

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

mauvaises nouvelles! pouvez-vous commenter pourquoi les choses sont comme ça? –

+1

Les types structurels fonctionnent normalement (voir http://infoscience.epfl.ch/record/138931/files/). Cela signifie simplement que cette construction n'est pas spéciale. –

2

J'ai lu que trop, et ont souvent voulu poser cette même question. Mais sur 2,8, je l'essayer:

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

Et quand je javap il:

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

On dirait de bonnes nouvelles, non?

Mise à jour Vous pouvez suivre cette optimisation en utilisant ce trac item

+0

Essayez la même chose avec '" ".foo'. Ou plus directement, 'scala -Xprint: jvm'. – retronym

+1

Mauvaises nouvelles: voir mon edit :( –

Questions connexes