2012-09-26 17 views
17

Recentemente mi sono imbattuto in uno strano (per me) messaggio di errore del compilatore. Si consideri il seguente codice:la classe A ha un parametro di tipo, ma il tipo B ha uno

trait Foo { 
    type Res <: Foo 
    type Bar[X <: Res] 
} 

class MyFoo extends Foo { 
    override type Res = MyFoo 
    override type Bar[X <: Res] = List[X] 
} 

type FOO[F <: Foo, R <: Foo, B[_ <: R]] = F { type Res = R; 
               type Bar[X <: R] = B[X] } 

def process[F <: Foo, R <: Foo, B[_ <: R]](f: FOO[F, R, B]) {} 

Ora, se voglio chiamare il metodo process devo scrivere in modo esplicito i parametri di tipo:

process[MyFoo, MyFoo, List](new MyFoo) // fine 

Se scrivo:

process(new MyFoo) 

o

process((new MyFoo): FOO[MyFoo, MyFoo, List]) 

ottengo il seguente messaggio di errore:

tipi desunti degli argomenti di tipo (MyFoo, MyFoo, Lista [X]) non sono conformi al tipo attesi dei parametri di tipo (di tipo F, tipo R, tipo B). Lista [X] 's parametri di tipo non corrisponde al tipo di parametri previsti B: Lista classe ha un parametro di tipo, ma di tipo B ha un

Perché purtroppo non si compilatore in grado di dedurre i tipi (anche se ho esplicitamente dichiarato loro al parametro call)? E cosa significa questo class List has one type parameter, but type B has one? Qualcosa ha uno, ma l'altro ha anche uno, ed è per questo che non si adattano insieme ???

+0

Sto usando scala 2.9.3-20120917-121530-db16547873 –

risposta

2

Se guardiamo a il compilatore di Scala, le fonti potrebbero aiutarci a capire qual è il problema. Non ho mai contribuito al compilatore di Scala, ma ho trovato le fonti molto leggibili e ho già studiato su questo.

La classe responsabile dell'inferenza di tipo è scala.tools.nsctypechecker.Infer che è possibile trovare semplicemente guardando nelle fonti del compilatore Scala per una parte del proprio errore. Scoprirete il seguente frammento:

/** error if arguments not within bounds. */ 
    def checkBounds(pos: Position, pre: Type, owner: Symbol, 
        tparams: List[Symbol], targs: List[Type], prefix: String) = { 
     //@M validate variances & bounds of targs wrt variances & bounds of tparams 
     //@M TODO: better place to check this? 
     //@M TODO: errors for getters & setters are reported separately 
     val kindErrors = checkKindBounds(tparams, targs, pre, owner) 

     if(!kindErrors.isEmpty) { 
     error(pos, 
      prefix + "kinds of the type arguments " + targs.mkString("(", ",", ")") + 
      " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." + 
      kindErrors.toList.mkString("\n", ", ", "")) 
     } 

Così ora il punto è capire il motivo per cui checkKindBounds(tparams, targs, pre, owner) restituisce quegli errori.Se si va giù per la catena di chiamata di metodo, si vedrà che i checkKindBounds chiamare un altro metodo

val errors = checkKindBounds0(tparams, targs, pre, owner, true) 

Vedrete il problema è collegato alla verifica limiti di tipo alto-kinded, alla riga 5784, all'interno checkKindBoundsHK:

if (!sameLength(hkargs, hkparams)) { 
     if (arg == AnyClass || arg == NothingClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded 
     else {error = true; (List((arg, param)), Nil, Nil) } // shortcut: always set error, whether explainTypesOrNot 
     } 

Il test non viene passato, sembra che nel mio debugger:

hkargs$1 = {[email protected]}"List()" 
arg$1 = {[email protected]}"class List" 
param$1 = {[email protected]}"type B" 
paramowner$1 = {[email protected]}"method process" 
underHKParams$1 = {[email protected]}"List(type R)" 
withHKArgs$1 = {[email protected]}"List()" 
exceptionResult12 = null 
hkparams$1 = {[email protected]}"List(type R)" 

Così sembra che ci sia una più alta param kinded, tipo R, ma non v'è fornire d valore per quello.

Se effettivamente tornare al al checkKindBounds, si vede che dopo il frammento:

val (arityMismatches, varianceMismatches, stricterBounds) = (
     // NOTE: *not* targ.typeSymbol, which normalizes 
     checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) 
    ) 

il arityMismatches contiene un elenco tuple, B. Ed ora anche si può vedere che il messaggio di errore è sbagliato:

tipi dedotte di tipo argomenti (MyFoo, MyFoo, Lista [X]) non conformi al tipo attesi dei parametri di tipo (tipo F, tipo R, tipo B). Lista [X] 's parametri di tipo non corrispondono attesi parametri di tipo di B: Lista classe ha un parametro di tipo, ma di tipo B ha ZERO

Infatti se si mette un punto di interruzione alla linea 5859 sul seguente chiamano

checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner, tparam.typeParams, tparamsHO) 

si può vedere che

tparam = {[email protected]}"type B" 
targ = {[email protected]}"List[X]" 

Conclusione:

0.123.516,410617 millions

Per qualche motivo, quando si tratta di tipi complessi di tipo superiore come il proprio, l'inferenza del compilatore Scala è limitata. Non so da dove provenga, forse vuoi mandare un bug al team del compilatore

0

Ho solo una vaga comprensione del funzionamento esatto del tipo inferrer in Scala, quindi considera queste idee non risposte definitive.

  1. L'inferenza di tipo ha problemi con l'inferire più di un tipo alla volta.

  2. si utilizza un tipo esistenziale, nella definizione di PIPPO, che si traduce in: esiste un tipo del genere, non so se questo sia compatibile con il tipo specifico data in MyFoo

Problemi correlati