Dichiarazione della questioneCome specializzarsi su un tipo di proiezione in Scala?
consideri un tipo T
che contiene un elemento tipo astratto A
:
trait T {
type A
}
mi piacerebbe creare una classe che prende un T0 <: T
come un parametro di tipo, ma si specializza sulla proiezione del tipo T0#A
. Ad esempio, nel seguito, il metodo foo
può essere specializzato?
class Foo[T0 <: T] {
def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}
Si noti che l'annotazione T0
con @specialized
non ottenere il risultato desiderato. C'è un buon modo per specializzarsi su foo
sulla proiezione del tipo T#A
?
Una soluzione limitata: ereditano dalla classe genitore specializzato con parametro in più
In questo caso particolare, ecco un modo di specializzarsi su 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]
ereditando dalla classe padre specializzata SpecializedFoo
, ci assicuriamo che lo Foo2.foo
sia specializzato.
verifica di specializzazione
Per verificare che Foo2.foo
, ma non Foo.foo
, è specializzata, li possiamo chiamare, con un esplicito T
dove T#A
è un doppio primitivo,
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)
}
Il bytecode può essere esaminato dal REPL con il comando ": 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
Si noti che la boxe appare in test1
ma non test2
.
Limitazioni
Modifica 7/9 Il trucco di cui sopra è più limitato di quanto ho capito in un primo momento. Non funzionerà affatto per specializzazione questo caso:
trait T {
type A
def x: A
def f: A => Double
}
class Foo[T0 <: T] {
def foo(t: T0) = t.f(t.x)
}
non vedo alcuna ragione per cui una (ipotetica) compilatore non poteva specializzarsi su A
in linea di principio; al solito, le versioni specializzate sarebbero utilizzabili solo quando uno specifico T#A
è noto al momento della compilazione. La soluzione pratica naturale è di sollevare A
in un parametro di tipo T
, ma mi chiedevo se potevo evitarlo.
L'idea è che voglio metodi specializzati su una attuazione classe 'barra si estende Foo [S]' 'in cui S <: T' è esplicitamente noto al momento della compilazione e' S # A' è un primitivo. So che questo può funzionare perché usando il trucco sopra (cioè una classe genitore 'SpecializedFoo' con un parametro di tipo extra) sono stato in grado di eliminare il pugilato, che ho verificato con": javap -v "nel REPL di Scala. Il codice di esempio nella domanda è ovviamente una semplificazione ... forse dovrei mettere più dettagli. –
Ho aggiornato la domanda per essere più esplicito su come appare la boxe e come può essere rimosso con la specializzazione. –