Statement of the question
Consider a type T that contains an abstract type member A:
trait T {
type A
}
I'd like to create a class that takes a T0 <: T as a type parameter, but specializes on the type projection T0#A. For example, in the following, can the method foo be specialized?
class Foo[T0 <: T] {
def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}
Note that annotating T0 with @specialized will not achieve the desired result. Is there is a good way to specialize foo on the type projection T#A?
A limited solution: inherit from specialized parent class with extra parameter
In this particular case, here's a way to specialize on 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]
By inheriting from the specialized parent class SpecializedFoo, we ensure that Foo2.foo is specialized.
Verification of specialization
To verify that Foo2.foo, but not Foo.foo, is specialized, we can call them with an explicit T where T#A is a primitive 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)
}
The bytecode can be examined from the REPL with the command ":javap -v Test",
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
Note that boxing appears in test1 but not test2.
Limitations
Edit 7/9 The trick above is more limited than I realized at first. It won't work at all for specializing this case:
trait T {
type A
def x: A
def f: A => Double
}
class Foo[T0 <: T] {
def foo(t: T0) = t.f(t.x)
}
I see no reason why a (hypothetical) compiler couldn't specialize on A in principle; a usual, the specialized versions would only be usable when a specific T#A is known at compile time. The natural practical solution is to lift A into a type parameter of T, but I was wondering if I could avoid that.