2012-04-27 32 views
6

Ten problem powstał w module, który piszę, ale zrobiłem minimalny przypadek, który wykazuje takie samo zachowanie.Dlaczego tutaj nie jest pisana inferencja typów?

class Minimal[T](x : T) { 
    def doSomething = x 
} 

object Sugar { 
    type S[T] = { def doSomething : T } 
    def apply[T, X <: S[T]] (x: X) = x.doSomething 
} 

object Error { 
    val a = new Minimal(4) 
    Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply 
    Sugar[Int, Minimal[Int]](a) // works as expected 
} 

Problemem jest to, że kompilator udaje się zorientować się parametr wewnętrzny do Minimal (Int), a następnie ustawia inne zdarzenia T do Nothing, co oczywiście nie jest zgodna apply. Są to z pewnością te same wartości, co T, ponieważ usunięcie pierwszego parametru powoduje, że drugi narzeka, że ​​T nie jest zdefiniowany.

Czy istnieje pewna niejednoznaczność, która oznacza, że ​​kompilator nie może wywnioskować pierwszego parametru, czy jest to błąd? Czy mogę obejść to z wdziękiem?

Dalsze informacje: Ten kod jest prostym przykładem próby syntaktycznego cukru. Oryginalny kod próbuje uczynić |(a)| znaczeniem modułu a, gdzie a jest wektorem. Najwyraźniej |(a)| jest lepsze niż pisanie |[Float,Vector3[Float]](a)|, ale niestety nie mogę użyć unary_|, aby było to łatwiejsze.

Rzeczywisty błąd:

inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]

Odpowiedz

9

Nie jest to błąd kompilatora Scala, ale jest to z pewnością ograniczenie wnioskowania typu Scala. Kompilator chce określić granicę na X, S[T], przed rozwiązaniem dla X, ale granica wspomina dotychczas nieograniczoną zmienną typu T, którą w związku z tym ustala na Nothing i przechodzi z tego miejsca. Nie wraca on po T po pełnym rozwiązaniu X ... obecnie typ wnioskowania zawsze przechodzi od lewej do prawej w tym przypadku.

Jeśli przykładem dokładnie reprezentuje rzeczywistą sytuację to istnieje prosty fix,

def apply[T](x : S[T]) = x.doSomething 

Tutaj T będzie wywnioskować, że takie Minimal zgodny S[T] bezpośrednio raczej niż za pośrednictwem zmiennej typu ograniczonego pośredniczącej.

Aktualizacja

Joshua rozwiązanie zapobiega również problem wnioskowania typu T, ale w zupełnie inny sposób.

def apply[T, X <% S[T]](x : X) = x.doSomething 

desugars do,

def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething 

Zmienne typu T i X Teraz można rozwiązać dla niezależnie (ponieważ T nie wspomina X „s granica).Oznacza to, że X jest natychmiastowo interpretowany jako Minimal, a T jest rozwiązywany jako część niejawnego wyszukiwania wartości typu X => S[T] w celu zaspokojenia niejawnego argumentu conv. conforms w scala.Predef wytwarza wartości tego formularza, a w kontekście gwarantuje, że dany argument typu Minimal, T zostanie wywiedziony jako Int. Możesz to zobaczyć jako instancję functional dependencies w pracy w Scali.

+0

Tak, to rozwiązanie działa dobrze w moim przypadku. Jest także czystszy niż rozwiązanie Joshua (przepraszam Joshua!). Czy możesz więc wyjaśnić, dlaczego rozwiązanie Joshua działa? – Dylan

+0

Należy zaktualizować odpowiedź, aby wyjaśnić, dlaczego rozwiązanie Joshua działa. –

4

Jest trochę niesamowitość z granicami na typach konstrukcyjnych, spróbuj użyć widoku związany na S [t] zamiast.

def apply[T, X <% S[T]] (x: X) = x.doSomething działa dobrze.

+1

Świetnie, to działa, ale na pewno nie powinno być żadnej różnicy między podejściami? Podejście w tej sprawie jest po prostu rzuceniem do superklasy, prawda? Czy to oznacza, że ​​ta poprawka jest tylko obejściem błędu w kompilatorze? – Dylan

+0

Ah, teraz widzę, 'S' nie jest superklasą - to tylko widok (w pewnym sensie). Tak więc widok jest bardziej odpowiedni, mimo że nie jest wymagany w większości przypadków. – Dylan