为什么不能优化这种隐式转换的情况?

发布于 2024-09-26 10:14:22 字数 473 浏览 6 评论 0原文

为什么 Scala 不能将以下内容优化:

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)

为什么在这种情况下必须使用结构类型?我希望 Scala 编译器执行此优化,因为以 b 风格编写实在是太丑陋了(因为,1.逻辑局部性丢失,2.你必须为这些额外的显式类发明不必要的名称) ,并且 a 的性能远低于 b

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

这样的小城市 2024-10-03 10:14:22

我认为可以,并且可以使用编译器插件来完成,看起来像

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

但我不知道是否有人编写了这样的插件...

更新:

如果我编译这个文件:

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

}

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

}
披肩女神 2024-10-03 10:14:22

我也读过,并且经常想问同样的问题。但在 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();
}

看起来是个好消息,不是吗?

更新您可以使用此trac 项目跟踪此优化

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?

Update You can track this optimization using this trac item

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文