![](/img/trans.png)
[英]How to specialize a type parameterized argument to multiple different types for in 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#A
的foo
的好方法?
有限的解决方案:从带有额外参数的特殊父 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#A
和T#B
(当Ops
也采用期望T
作为参数的更高种类的类型时,这是必要的)。
我看不出这怎么可能奏效。 编译 class 时进行了专门化,当时还不知道A
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.