2011-07-08 19 views
9

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 Aw 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ąć.

Odpowiedz

1

To jest ograniczenie kompilatora; jeden nie może generalnie specjalizować się na elementach parametru typu. Jednak proponowana Sztuką jest wystarczająco dobre dla moich celów:

trait Types { 
    type A 
    type B 
} 

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

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

ten sposób cecha Ops dostaje specjalizuje ponieważ dziedziczy implementacje w wyspecjalizowanych cechy GenOps. Moja motywacja polega na tym, że chcę, aby cecha Ops przyjmowała jeden parametr o typie T, a nie zarówno T#A, jak i T#B (staje się to konieczne, gdy Ops przyjmuje również typ wyższego typu, który oczekuje jako parametr parametru T).

1

Nie widzę, jak to mogłoby działać. Specjalizacja jest wykonywana podczas kompilacji klasy, aw tym czasie A nie jest znana.

+0

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. –

+0

Uaktualniłem pytanie, aby wyjaśnić, jak wygląda boks i jak można go usunąć ze specjalizacją. –

Powiązane problemy