2009-07-26 10 views
8

Supponiamo di avere due classi, Input e Output, progettate per essere connesse tra loro. Output produce valori di qualche tipo e Input li consuma.Perché Scala non può inferire il parametro type in questo esempio?

class Input[T] { 
    var output: Option[Output[_ <: T]] = None 
} 
class Output[T] { 
    var input: Option[Input[_ >: T]] = None 
} 

Va bene se una coppia Input e Output non operano sullo stesso tipo di valore fino a quando il parametro di tipo Input è un supertipo del parametro Output tipo. Si noti che il parametro type in entrambe le classi è invariante; nelle versioni reali è usato in entrambe le posizioni co- e controvarianti.

Ho un metodo connect altrove, che stabilisce un collegamento tra un Input/Output coppia:

def connect[T](output: Output[T], input: Input[_ >: T]) = { 
    output.input = Some(input) 
    input.output = Some(output) 
} 

Se chiamo questo metodo come di seguito, ottengo un errore di tipo:

val out = new Output[String] 
val in = new Input[AnyRef] 
connect(out, in) 

L'errore è:

test.scala:17: error: type mismatch; 
found : Output[String] 
required: Output[AnyRef] 
    connect(out, in) 
     ^

Posso risolvilo scrivendo il parametro type (in questo caso, scriverei connect[String], ma penso che il compilatore dovrebbe essere in grado di capirlo. Come posso modificare il metodo connect in modo che il parametro type venga inferito automaticamente?


Edit: Per ora, ho fatto connect un metodo di Output così diventa il parametro di tipo automatico. Questo ha anche il vantaggio che posso usare la notazione infisso out connect in, ma il design sembra un po 'imbarazzante.

Sono ancora interessato al motivo per cui il compilatore mostra questo comportamento. Mi sento come dovrebbe essere in grado di inferire il parametro di tipo. Funziona effettivamente come specificato?

+0

intendevi "non operare * su * lo stesso tipo di valore" –

+0

hai provato a porre la domanda alla mailing list di Scala? – GClaramunt

risposta

6

Avrete a volte ottenere risultati migliori se si utilizzano più elenchi di parametri:

def connect[T](output: Output[T])(input: Input[_ >: T]) = { 
    output.input = Some(input) 
    input.output = Some(output) 
} 

connect(out)(in) 

... e in effetti, in questo caso, funziona.

+3

Puoi spiegare perché è così? * "a volte ottenere risultati migliori" * non sembra molto deterministico! –

+6

Purtroppo, il tipo di inferenza non è specificato e talvolta non è deterministico. –

0

Potrei essere totalmente sbagliato, ma penso che il problema sia quando si uniscono l'input e l'output: l'input ha un'uscita limitata a un sottotipo di T, ma l'output ha un input limitato a un supertipo di T, l'unico tipo che può soddisfare entrambe le condizioni è T.

Input[T] -> Output[ _ <: T ] 
Output[Q] -> Input[ _ >: Q ] 

Quando si crea l'ingresso con l'uscita (sostituendo Q con _ <: T) si ottiene:

Input[T]->Input[ _ >: [_ <: T] ] 

Lo stesso che

ingresso [T] -> ingresso [_ <: T <: _]

qui la mancata corrispondenza del tipo

+0

Nel mio esempio, String e AnyRef soddisfano i miei vincoli. L'output produce stringhe e richiede un input che utilizza un supertipo di stringa. L'Input consuma AnyRefs e richiede un Output che produca un sottotipo di AnyRef. Poiché String è un sottotipo di AnyRef, i vincoli sono soddisfatti. –

+0

Il problema è che esiste esattamente un parametro di tipo appropriato per la connessione e che è il parametro type dell'output. Quando lo specifichi esplicitamente, funziona perfettamente. Se non lo faccio, cerca di usare il parametro type dell'input e ottengo un errore di tipo sull'argomento Output. –

+0

Spiacente, ho errato l'input [_>: T] nella firma di connect [T] ( – GClaramunt

0

Attualmente inferene di tipo scala non può gestire "ricorsione" per ora. Quindi se il tipo di argomento può essere dedotto solo in collocazione con altri argomenti scala non riesce a dedurre. Ma se usi una lista di argomenti diversi scala f(a)(b)(c,d) dedurrà i tipi lista per lista, quindi funziona meglio.

PS E 'semplicistico, ma può darti qualche idea.