2011-07-08 10 views
9

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

risposta

1

Questa è una limitazione del compilatore; uno non è in grado di generalmente specializzato su elementi di un parametro di tipo. Tuttavia, il trucco proposto è abbastanza buono per i miei scopi:

trait Types { 
    type A 
    type B 
} 

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

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

In questo modo il tratto Ops viene specializzata perché eredita le implementazioni specializzate nel tratto GenOps. La mia motivazione è che voglio il tratto Ops per prendere un singolo parametro di tipo T, anziché entrambi T#A e T#B (questo diventa necessario quando lo Ops accetta anche un tipo di tipo più elevato che si aspetta T come parametro).

1

Non riesco a vedere come possa funzionare. La specializzazione viene eseguita durante la compilazione della classe e, in quel momento, A non è noto.

+0

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

+0

Ho aggiornato la domanda per essere più esplicito su come appare la boxe e come può essere rimosso con la specializzazione. –

Problemi correlati