mam te modele:Rozwiązywanie typy w F-ograniczonego polimorfizmu
trait Vehicle[T <: Vehicle[T]] { def update(): T }
class Car extends Vehicle[Car] { def update() = new Car() }
class Bus extends Vehicle[Bus] { def update() = new Bus() }
Gdybym uzyskać instancję Vehicle[Car]
i powołać update()
będę dostać Car
. Od Car
rozciąga Vehicle[Car]
(lub po prostu, samochodów jest pojazdu [samochodu]), mogę bezpiecznie ustawić typ wyniku wyraźnie do Vehicle[Car]
:
val car = new Car
val anotherCar = car.update()
val anotherCarAsVehicle: Vehicle[Car] = car.update() // works as expected
Ale jeśli chcę, powiedzmy, umieścić instancje z Car
i Bus
razem w jednej listy, to muszę ustawić typ listy do Vehicle[_ <: Vehicle[_]]
(o listę po prostu Vehicle[_]
i powołując update()
na elemencie przyniesie Any
, ale chcę, aby móc korzystać update()
więc muszę używać typ F-bounded). Korzystanie rodzajów egzystencjalne śruby up relacje typu, bo kiedyś pobrać leżących u podstaw samochód/autobus z pojazdu, nie mogę już oddanych do pojazdu, ponieważ ... dobrze, to po prostu jakiś rodzaj egzystencjalnej:
val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)
val car = seq.head.update()
val carAsVehicle: Vehicle[_ <: Vehicle[_]] = seq.head.update() // fails to compile
Tak , Vehicle
jest sparametryzowany z pewnym typem T
, który jest podtypem Vehicle[T]
. Kiedy wyrywam T
(przy użyciu update()
), w przypadku typów betonu jest ok - np. jeśli wyrwę Car
, mogę bezpiecznie twierdzić, że wyrzuciłem Vehicle[Car]
ponieważ Car <: Vehicle[Car]
. Ale jeśli wyrywam typ egzystencjalny, nie mogę z tym nic zrobić. Wcześniejszy przykład zadziałał, ponieważ Car
to Vehicle[Car]
, ale w tym przypadku _
nie jest Vehicle[_]
.
Aby określić moje konkretne pytanie: w przypadku modeli wymienionych powyżej (pojazd, samochód, autobus), czy istnieje sposób, aby to osiągnąć?
def sameType[T, U](a: T, b: U)(implicit evidence: T =:= U) = true
val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus)
sameType(seq.head.update +: seq.tail, seq) // true
Pamiętaj, że możesz zmienić danych cech, klas i rodzaj seq
, ale istnieje jedno ograniczenie: update()
musi powrócić T
, nie Vehicle[T]
.
Wiem, że używanie bezkształtnego HList
rozwiązałoby problem, ponieważ nie musiałbym używać typów egzystencjalnych (po prostu miałbym listę samochodów i autobusów, a informacje o tym byłyby zachowane). Ale zastanawiam się nad tym konkretnym przypadkiem użycia z prostym List
.
EDIT:
@RomKazanova tak, że będzie działać oczywiście, ale trzeba zachować ten sam typ przed i po update()
(oto upvote za wysiłek chociaż;)).
Uważam, że nie jest to możliwe bez HList lub podobnej struktury danych, ponieważ ujednolicenie samochodów i autobusów zmusza nas do użycia typu pojazdu, który traci informacje o tym, czy jego podstawowym typem był samochód, autobus lub coś innego (wszystko, co możemy wiedzieć, to że był to jakiś typ _ <: Vehicle
). Ale chcę się z wami skontaktować.
myślałem, że 'Lista [pojeździe [_ <: Pojazd [_]]]' jest tego samego typu co 'listy [pojazdu [ T] forSome {type T <: Vehicle [T]}] '. Jako ogólna zasada, nie miałbym nic przeciwko używaniu 'forSome' wszędzie, ale słyszałem, że jest eksmitowany w Scali 2.13 lub 2.14. W każdym razie, dziękuję bardzo, jest to dokładnie takie rozwiązanie, na które miałem nadzieję. – slouc
Usunąłem wszystkie ostrzeżenia egzystencjalne z mojego transkrypcji REPL. Myślę więc, że jest to rodzaj egzystencjalny, który jest niewyrażalny tylko za pomocą symboli wieloznacznych. Nie jestem pewien * jeśli * zostanie całkowicie usunięty, ale jeśli tak, to * wydaje mi się, że najwcześniej będzie, gdy Dotty zmieni się w Scala 3.0 lub coś w tym stylu. –
BTW Przypadkowo Cię popuściłem :) naprawiono – slouc