I have these models:
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() }
If I get an instance of a Vehicle[Car] and call update() , I get a Car . Since Car extends Vehicle[Car] (or is simply put, Car is Vehicle [Car]), I can safely set the result type explicitly to Vehicle[Car] :
val car = new Car val anotherCar = car.update() val anotherCarAsVehicle: Vehicle[Car] = car.update() // works as expected
But if I want to, say, put Car and Bus instances together in one list, then I need to set the list type to Vehicle[_ <: Vehicle[_]] (having the list just Vehicle[_] and calling update() on the element will give Any but I want to be able to use update() , so I need to use an F-restricted type). Using existential types spins type relationships because, as soon as I pick up the base car / bus from the vehicle, I can no longer drop it onto the Car, because ... well, it's just some kind of existential type:
val seq = List[Vehicle[_ <: Vehicle[_]]](new Car, new Bus) val car = seq.head.update() val carAsVehicle: Vehicle[_ <: Vehicle[_]] = seq.head.update() // fails to compile
So, Vehicle parameterized by some type of T , which is a subtype of Vehicle[T] . When I pull out T (using update() ), in the case of specific types, this is normal - for example. if I rip out of Car , I can safely say that I pulled out a Vehicle[Car] , because Car <: Vehicle[Car] . But if I destroy the existential type, there is nothing I can do about it. The previous example worked because Car is Vehicle[Car] , but in this case _ not Vehicle[_] .
To point out my specific question : for the models above (Vehicle, Car, Bus), is there any way to achieve this?
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
Note that you can change the specified traits, classes, and type of seq , but there is one limitation: update() must return T , not Vehicle[T] .
I know that using a shapeless HList would solve the problem, since I would not have to use existential types (I would just have a list of cars and buses, and this type information would be saved). But I'm interested in this particular use case for a simple List .
EDIT :
@RomKazanova yes, this will work, of course, but I need to keep the same type before and after update() (here is an excerpt for efforts, though;)).
I believe that this is not possible without an HList or similar data structure, because combining cars and buses forces us to use a vehicle type that loses information about whether its base type is Car, Bus or something else (all that we can know, it was some type of _ <: Vehicle ). But I want to check with you guys.