2012-09-26 24 views
17

Recientemente me encontré con un mensaje de error del compilador extraño (para mí). Considere el siguiente código:clase A tiene un parámetro de tipo, pero el tipo B tiene 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]) {} 

Ahora, si quiero llamar a la process método que tengo que escribir explícitamente los parámetros de tipo:

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

Si escribo:

process(new MyFoo) 

o

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

Me sale el siguiente mensaje de error:

tipos inferidos de los argumentos de tipo (myFoo, myFoo, lista [X]) no se ajusten a los tipos esperados de los parámetros de tipo (tipo F, Tipo I, Tipo B) Lista [X] 's parámetros de tipo no coinciden con los parámetros esperados de tipo B: Lista de clases tiene un parámetro de tipo, pero de tipo B tiene un

Por qué NO ES el compilador capaz de inferir los tipos (aunque explícitamente ellos en el parámetro de llamada)? ¿Y qué significa eso class List has one type parameter, but type B has one? Algo tiene uno, pero el otro tiene también uno, y es por eso que no encajan?

+0

Estoy usando scala 2.9.3-20120917-121530-db16547873 –

Respuesta

2

Si miramos a el compilador de Scala, las fuentes podrían ayudarnos a entender cuál es el problema. Nunca contribuí con el compilador de Scala, pero encontré las fuentes muy legibles y ya investigué sobre eso.

La clase responsable de la inferencia de tipo es scala.tools.nsctypechecker.Infer que puede encontrar simplemente buscando en las fuentes del compilador de Scala una parte de su error. Encontrará el siguiente fragmento:

/** 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", ", ", "")) 
     } 

Así que ahora el punto es entender por qué checkKindBounds(tparams, targs, pre, owner) devuelve esos errores.Si vas por la cadena de llamada a un método, se verá que los checkKindBounds llamar a otro método

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

Vas a ver el problema está conectado a la comprobación de límites del tipo de mayor kinded, en la línea 5784, en el interior 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 
     } 

La prueba no se pasa, parece que en mi depurador:

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)" 

Así que parece como que hay un alto parámetro kinded, tipo R, pero sin ofrecer es d valor para eso.

, si intenta ir de nuevo a la de checkKindBounds, se ve que después del fragmento:

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

la arityMismatches contiene una lista de tuplas, B. Y ahora también se puede ver que el mensaje de error es un error:

tipos inferidos de los argumentos de tipo (myFoo, myFoo, lista [X]) no se ajustan a los tipos esperados de los parámetros de tipo (tipo F, tipo R, tipo B). Lista [X] 's parámetros de tipo no coincide con el esperado parámetros de tipo de B: Lista de clases tiene un parámetro de tipo, pero de tipo B tiene CERO

De hecho, si se pone un punto de interrupción en la línea 5859 en el siguiente llaman

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

se puede ver que

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

Conclusión:

Por alguna razón, cuando se trata de tipos complejos de alto nivel como el suyo, la inferencia del compilador de Scala es limitada. No sé de dónde viene, quizás quiera enviar un error al equipo del compilador

0

Solo tengo una vaga comprensión del funcionamiento exacto del tipo inferrer en Scala, así que considere estas ideas como respuestas no definitivas.

  1. Tipo inferring tiene problemas para inferir más de un tipo a la vez.

  2. utiliza un tipo existencial en la definición de FOO, que se traduce en: existe un tipo tal, no está seguro de si esto es compatible con el tipo específico dado en myFoo

Cuestiones relacionadas