komunikat z pytaniemJak specjalizować się na projekcji typu w Scali?
Rozważmy typ T
który zawiera element abstrakcyjny typ A
:
trait T {
type A
}
chciałbym utworzyć klasę, która pobiera T0 <: T
jako parametr typu, ale specjalizuje się na projekcji typu T0#A
. Na przykład, poniżej, czy metoda może być wyspecjalizowana?
class Foo[T0 <: T] {
def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}
Zauważ, że opisywanie T0
z @specialized
nie przyniesie pożądanego rezultatu. Czy istnieje dobry sposób na specjalizację foo
na projekcji typu T#A
?
Ograniczona rozwiązanie: dziedziczą z wyspecjalizowanej klasy nadrzędnej z dodatkowym parametrem
W tym konkretnym przypadku, oto sposób na specjalizację na 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]
przez dziedziczenie z wyspecjalizowanej klasy nadrzędnej SpecializedFoo
, zapewniamy, że wyspecjalizowaliśmy się w Foo2.foo
.
Weryfikacja specjalizacji
Aby sprawdzić, czy Foo2.foo
, ale nie Foo.foo
, specjalizuje możemy nazwać je z wyraźną T
gdzie T#A
jest prymitywne Podwójne,
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)
}
Kod bajtowy może być zbadane z REPL za pomocą polecenia ": 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
Należy zauważyć, że boks pojawia się w test1
, ale nie pod numerem test2
.
Ograniczenia
Edycja 7/9 nad Sztuką jest bardziej ograniczona niż zdałem sobie sprawę w pierwszej kolejności. To nie będzie działać w ogóle specjalizujący tym przypadku:
trait T {
type A
def x: A
def f: A => Double
}
class Foo[T0 <: T] {
def foo(t: T0) = t.f(t.x)
}
nie widzę powodu, dlaczego (hipotetyczny) kompilator nie mógł specjalizować na A
w zasadzie; zwykłe, wyspecjalizowane wersje byłyby użyteczne tylko wtedy, gdy specyficzny T#A
jest znany w czasie kompilacji. Naturalnym praktycznym rozwiązaniem jest podniesienie A
do parametru typu T
, ale zastanawiałem się, czy mogę tego uniknąć.
Chodzi o to, że chcę, aby wyspecjalizowane metody na implementującej 'class Bar rozszerzały Foo [S]' gdzie 'S <: T' jest jawnie znane podczas kompilacji, a' S # A' jest prymitywne. Wiem, że to może zadziałać, ponieważ używając powyższej sztuczki (tj. Klasy rodzica 'SpecializedFoo' z dodatkowym parametrem typu) mogłem wyeliminować boks, który zweryfikowałem za pomocą": javap -v "w Scala REPL. Przykładowy kod w pytaniu jest oczywiście uproszczeniem ... może powinienem podać więcej szczegółów. –
Uaktualniłem pytanie, aby wyjaśnić, jak wygląda boks i jak można go usunąć ze specjalizacją. –