2012-09-26 18 views
17

Niedawno natknąłem się na dziwny (dla mnie) komunikat o błędzie kompilatora. Rozważmy następujący kod:klasa A ma jeden parametr, ale typ B ma jeden:

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]) {} 

Teraz, jeśli chcę wywołać metodę process muszę wyraźnie napisać parametry Typ:

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

Jeśli piszę:

process(new MyFoo) 

lub

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

pojawia się następujący komunikat o błędzie:

wywnioskować rodzaje argumentów typu (MyFoo, MyFoo lista [X]) nie są zgodne z oczekiwanymi rodzaju parametrów typu (typ F, typ R, typ B). Lista parametrów typu [X] nie jest zgodna z oczekiwanymi parametrami typu B: klasa Lista ma jeden parametr, ale typ B ma jeden:

Dlaczego kompilator nie jest w stanie wywnioskować typów (chociaż wyraźnie zaznaczono ich w parametrze call)? A co to znaczy class List has one type parameter, but type B has one? Coś ma jedno, ale drugie ma jedną i to dlatego nie pasują do siebie ???

+0

Używam scala 2.9.3-20120917-121530-db16547873 –

Odpowiedz

2

Jeśli spojrzymy na kompilator Scala, źródła mogą pomóc nam zrozumieć, na czym polega problem. Nigdy nie brałem udziału w kompilacji Scali, ale znalazłem źródła bardzo czytelne i już to zbadałem.

Klasa odpowiedzialna za wnioskowanie o typ to scala.tools.nsctypechecker.Infer, którą można znaleźć po prostu patrząc w źródłach kompilatora Scala jako część błędu. Dowiesz się następujący fragment:

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

Więc teraz punktem jest zrozumienie, dlaczego checkKindBounds(tparams, targs, pre, owner) zwraca te błędy.Jeśli pójdziesz w dół łańcucha metoda połączenia, widać, że checkKindBounds wywołać inną metodę

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

Zobaczysz problem jest podłączony do sprawdzania granic wyższej kinded typu, na linii 5784, wewnątrz checkKindBoundsHK:

wydaje
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 
     } 

Test nie jest przekazywana, że ​​w moim debugera:

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

Wygląda więc na to jak jeden jest wyższy kinded param typu R, ale nie ma to zapewnić d wartość za to.

Jeśli rzeczywiście wrócić do do checkKindBounds, widać, że po fragmencie:

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

arityMismatches zawiera listę krotka, B. I teraz można również zobaczyć, że komunikat o błędzie jest źle:

wywnioskowaną typy argumentów typu (MyFoo, MyFoo, lista [X]) nie zgodne z oczekiwanymi rodzaju parametrów typu (typu f typu R typu B). Lista [X] 's parametry typu nie pasują oczekiwanych parametrów typu B: Lista klasa ma jeden parametr typu, ale typ B ma ZERO

W rzeczywistości, jeśli umieścić punkt przerwania na linii 5859 na następujących zadzwoń

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

widać, że

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

Wniosek:

Z jakiegoś powodu, kiedy mamy do czynienia ze złożonymi typami o wyższych typach, takimi jak wasze, wnioskowanie kompilatora Scala jest ograniczone. Nie wiem skąd się to bierze, być może chcesz wysłać błąd do zespołu kompilatora

0

Mam tylko mgliste zrozumienie dokładnego działania infertera typu w Scali, więc rozważ te pomysły, a nie ostateczne odpowiedzi.

  1. Inferring typu ma problemy z wnioskowanie więcej niż jeden typ na raz.

  2. użyć typ egzystencjalne w definicji FOO, co przekłada się na: istnieje rodzaj takie, nie wiem, czy jest to zgodne z określonym typie podanym w MyFoo

Powiązane problemy