2009-07-26 38 views
8

Załóżmy, że mam dwie klasy: Input i Output, które są przeznaczone do połączenia ze sobą. Output wytwarza wartości pewnego rodzaju, a Input je konsumuje.Dlaczego Scala nie może podać parametru typu w tym przykładzie?

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

Jest w porządku, jeśli Input i Output para nie działają na tym samym rodzaju wartości, o ile typ parametru Input jest supertypem parametru Output typu. Zauważ, że parametr type w obu klasach jest niezmienny; w wersjach rzeczywistych jest używany zarówno w pozycjach współwystępujących, jak i przeciwstawnych.

Mam connect metodę gdzie indziej który ustanawia związek między Input/Output pary:

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

Gdybym wywołać tę metodę, jak poniżej, pojawia się błąd typu:

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

Błąd:

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

Mogę rozwiązać ten problem, wypisując parametr type (w tym przypadku napisałbym connect[String], ale myślę, że kompilator powinien być w stanie to dla mnie wyliczyć. Jak mogę zmienić metodę connect tak, aby parametr typu został wywnioskowany automatycznie?


Edit: Na razie zrobiłem connect metoda Output więc robi parametr typu automatycznie. Ma to również dodatkową zaletę, że mogę używać notacji infix out connect in, ale projekt wydaje się trochę niezręczny.

Nadal interesuje mnie, dlaczego kompilator wykazuje takie zachowanie. Czuję, że powinien być w stanie wywnioskować parametr typu. Czy to rzeczywiście działa zgodnie z określonymi?

+0

czy masz na myśli "nie dzialaj * przy * tej samej wartości" –

+0

Czy próbowałeś zadać to pytanie na liście mailingowej Scala? – GClaramunt

Odpowiedz

6

Czasem uzyskać lepsze rezultaty, jeśli używasz wielu list parametrów:

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

connect(out)(in) 

... i rzeczywiście w tym przypadku, to działa.

+3

Czy możesz rozwinąć, dlaczego tak jest? * "czasami osiągają lepsze wyniki" * nie brzmi zbyt deterministycznie! –

+6

Niestety, inferencer typów nie jest określony, a czasami nie jest również deterministyczny. –

0

Mogę być całkowicie błędny, ale myślę, że problem występuje, gdy łączysz wejście i wyjście: wejście ma wyjście ograniczone do podtypu T, ale wyjście ma wejście ograniczone do nadtypu T, jedynym typem, który może spełnić oba warunki, jest T.

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

Podczas tworzenia wejście z wyjściem (zamieniając Q _ <: T) można uzyskać:

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

takie same jak

wsad [t] -> Wejście [_ < T <: _]

Stąd niezgodności typu

+0

W moim przykładzie ciąg i AnyRef spełniają moje ograniczenia. Wyjście generuje łańcuchy i wymaga wejścia, które pochłania pewien nadmiarowy ciąg. Wejście zużywa AnyRefs i wymaga wyjścia, które produkuje jakiś podtyp AnyRef. Ponieważ String jest podtypem AnyRef, ograniczenia są spełnione. –

+0

Problem polega na tym, że istnieje dokładnie jeden odpowiedni typ parametru dla połączenia i jest to parametr typu wyjścia. Kiedy wyraźnie to określam, działa dobrze. Jeśli tego nie zrobię, spróbuje użyć parametru type z Input i otrzymam błąd typu na argumencie Output. –

+0

Przepraszamy, źle odczytałem wejście [_>: T] w sygnaturze połączenia [T] ( – GClaramunt

0

W rzeczywistości incina typu scala nie może obsługiwać "rekursji" na teraz. Więc jeśli typ argumentu można wnioskować tylko w kolokacji z innym argumentem scala nie można wnioskować. Ale jeśli użyjesz listy argumentów różnych, scala f(a)(b)(c,d) wywnioskuje listę typów według listy, więc z czasem działa lepiej.

PS Jest to uproszczenie, ale może dać ci jakąś wskazówkę.

Powiązane problemy