2013-02-06 9 views
11

Ho alcuni problemi con Scala per dedurre il tipo giusto da un tipo di proiezione.Come dedurre il giusto tipo di parametro da un tipo di proiezione?

Si consideri il seguente:

trait Foo { 
    type X 
} 

trait Bar extends Foo { 
    type X = String 
} 

def baz[F <: Foo](x: F#X): Unit = ??? 

Poi il seguente compila bene:

val x: Foo#X = ???  
baz(x) 

ma la seguente non verrà compilato:

val x: Bar#X = ???  
baz(x) 

Scala vede il "tipo String sottostante "per x, ma ha perso le informazioni che x è un Bar#X. Funziona bene se io annotare il tipo:

baz[Bar](x) 

C'è un modo per rendere Scala dedurre il parametro di tipo giusto per baz?
In caso contrario, qual è la risposta generale che rende impossibile?

+2

Non una risposta, ma vale la pena notare che se si digita 'x' utilizzando un designatore di tipo anziché una proiezione di tipo, funziona, ad esempio per es. 'oggetto BAR estende Bar; val x: BAR.X = "a"; Baz (x) '. –

+3

Vale anche la pena notare: si può convincere il compilatore che si desidera veramente che 'x' venga digitato come qualcosa più o meno come' Bar # X' con l'incredibilmente brutto 'val x: bX forSome {val b: Bar} =" a ": bX forSome {val b: Bar}'. –

+0

Sembra strano caso d'uso. Perchè vuoi fare questo? –

risposta

2

Il programma viene compilato con l'aggiunta di questa conversione implicita nel contesto:

implicit def f(x: Bar#X): Foo#X = x 

Come questa conversione implicita è corretto per ogni F <: Foo, mi chiedo perché il compilatore non lo fa da solo.

1

È inoltre possibile:

trait Foo { 
    type X 
} 
trait Bar extends Foo { 
    type X = String 
} 
class BarImpl extends Bar{ 
    def getX:X="hi" 
} 
def baz[F <: Foo, T <: F#X](clz:F, x: T): Unit = { println("baz worked!")} 
val bi = new BarImpl 
val x: Bar#X = bi.getX 
baz(bi,x) 

ma:

def baz2[F <: Foo, T <: F#X](x: T): Unit = { println("baz2 failed!")} 
baz2(x) 

fallisce con:

test.scala:22: error: inferred type arguments [Nothing,java.lang.String] do not conform to method baz2's type parameter bounds [F <: this.Foo,T <: F#X] 
baz2(x) 
^ 
one error found 

Credo che in fondo, F <: Foo dice al compilatore che F deve essere un sottotipo di Foo, ma quando ottiene una X non sa quale classe il tuo particolare X proviene da. La tua X è solo una stringa e non mantiene le informazioni che puntano a Bar.

Nota che:

def baz3[F<: Foo](x : F#X) = {println("baz3 worked!")} 
baz3[Bar]("hi") 

funziona anche. Il fatto che tu abbia definito una val x: Bar # X = ??? significa solo che ??? è limitato a qualunque Bar # X potrebbe trovarsi in fase di compilazione ... il compilatore sa che Bar # X è String, quindi il tipo di x è solo una stringa non diversa da qualsiasi altra stringa.

+0

Sì, il compilatore vede solo 'String' e perde il contesto' Foo' nonostante annoti il ​​tipo con 'Bar # X' invece di' String'. La mia unica soluzione finora è di astrarre sempre su 'Foo' e fornire' Bar' alla fine. Ma questo è davvero un problema per me in quanto limita quello che posso fare 'baz' ... (nel mio caso,' baz' è anche implicito) – betehess

+0

Giusto. potresti pensarlo in questo modo: type X è un membro di Foo, un po 'come con altre variabili statiche rispetto a membri in Java. Cercare di accedervi senza un'istanza è come provare ad accedere a una variabile membro senza un'istanza. – Brian

+0

In: 'tratto HasType {tipo X} classe Foo (val t: HasType) {tipo A = t.X}'. Potresti avere molti 'Foo' con diversi' A', ognuno dipendente dal particolare 't' passato (istanza di' HasType'). Il tipo è dinamico e quindi deve essere specificato in qualche modo. Foo # A nell'esempio qui non dice più a te o al compilatore di 'A' che Person.age ti dirà dell'età dell'istanza di una persona. Un'istanza 't' fornisce le informazioni sul tipo. – Brian

Problemi correlati