繁体   English   中英

如何专注于 Scala 中的类型投影?

[英]How to specialize on a type projection in Scala?

问题陈述

考虑一个包含抽象类型成员A的类型T

trait T {
  type A
}

我想创建一个 class ,它采用T0 <: T作为类型参数,但专注于类型投影T0#A 例如,在下面,方法foo可以特化吗?

class Foo[T0 <: T] {
  def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}

请注意,使用@specialized注释T0不会达到预期的结果。 有没有一种专门针对类型投影T#Afoo的好方法?

有限的解决方案:从带有额外参数的特殊父 class 继承

在这种特殊情况下,这是一种专注于T0#A方法:

trait SpecializedFoo[@specialized A0, T0 <: T] {
  def foo(a: A0, f: A0 => A0) = f(a)
}
class Foo2[T0 <: T] extends SpecializedFoo[T0#A, T0]

通过从专用父 class SpecializedFoo继承,我们确保Foo2.foo是专用的。

专业化验证

为了验证Foo2.foo而不是Foo.foo是特化的,我们可以使用显式T调用它们,其中T#A是原始 Double,

trait ExplicitT extends T {
  type A = Double
}

object Test {
  def test1 = (new Foo[ExplicitT]).foo(1.0, _ + 1.0)
  def test2 = (new Foo2[ExplicitT]).foo(1.0, _ + 1.0)
}

可以使用命令“:javap -v Test”从 REPL 检查字节码,

public double test1();
  Code:
   Stack=4, Locals=1, Args_size=1
   0:   new #16; //class Foo
   3:   dup
   4:   invokespecial   #18; //Method Foo."<init>":()V
   7:   dconst_1
   8:   invokestatic    #24; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
   11:  new #26; //class Test$$anonfun$test1$1
   14:  dup
   15:  invokespecial   #27; //Method Test$$anonfun$test1$1."<init>":()V
   18:  invokevirtual   #31; //Method Foo.foo:(Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;
   21:  invokestatic    #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
   24:  dreturn
  LineNumberTable: 
   line 13: 0


public double test2();
  Code:
   Stack=5, Locals=1, Args_size=1
   0:   new #38; //class Foo2
   3:   dup
   4:   invokespecial   #39; //Method Foo2."<init>":()V
   7:   dconst_1
   8:   new #41; //class Test$$anonfun$test2$1
   11:  dup
   12:  invokespecial   #42; //Method Test$$anonfun$test2$1."<init>":()V
   15:  invokeinterface #48,  4; //InterfaceMethod SpecializedFoo.foo$mcD$sp:(DLscala/Function1;)D
   20:  dreturn
  LineNumberTable: 
   line 14: 0

请注意,拳击出现在test1而不是test2

限制

编辑 7/9上面的技巧比我一开始意识到的要有限。 对于专门处理这种情况,它根本不起作用:

trait T {
  type A
  def x: A
  def f: A => Double
}

class Foo[T0 <: T] {
  def foo(t: T0) = t.f(t.x)
}

我看不出为什么(假设的)编译器原则上不能专注于A 通常,只有在编译时知道特定的T#A时,专用版本才可用。 自然而实际的解决方案是将A提升为T的类型参数,但我想知道是否可以避免这种情况。

这是编译器限制; 人们通常不能专门研究类型参数的元素。 但是,建议的技巧足以满足我的目的:

trait Types {
  type A
  type B
}

trait GenOps[@specialized A, @specialized B] {
  ...
}

trait Ops[T <: Types] extends GenOps[T#A, T#B]

这样,特质Ops变得专业化,因为它继承了特质GenOps中的专业化实现。 我的动机是我希望 trait Ops采用单个类型参数T ,而不是同时采用T#AT#B (当Ops也采用期望T作为参数的更高种类的类型时,这是必要的)。

我看不出这怎么可能奏效。 编译 class 时进行了专门化,当时还不知道A

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM