Reputation: 9610
This is a follow-up to a previous question where I had a trait Garage
with a type member CarType
, which itself had a type member FuelType
, and I needed a function refuel
that could take an instance of CarType
as the first argument and an instance of the first argument's FuelType
as the second argument.
The answer, the two traits below, was to give Car
a representation type C <: Car[C]
. The problem that I now have is that I can't figure out how to define the type parameter on the concrete classes implementing Garage
, e.g. ConcreteGarage
below.
trait Fuel
trait Garage {
type CarType <: Car[CarType]
def cars: Seq[CarType]
def copy(cars: Seq[CarType]): Garage
def refuel(car: CarType, fuel: CarType#FuelType): Garage = copy(
cars.map {
case `car` => car.refuel(fuel)
case other => other
})
}
trait Car[C <: Car[C]] {
type FuelType <: Fuel
def fuel: FuelType
def copy(fuel: C#FuelType): C
def refuel(fuel: C#FuelType): C = copy(fuel)
}
class ConcreteGarage(val cars: Seq[ConcreteGarage#CarType]) extends Garage {
type CarType = Car[CarType] // Nope
//type CarType = Car[Any] // Nope
//type CarType = Car[Nothing] // Nope
//type CarType = Car[Car] // Nope
//type CarType <: Car[CarType] // Nope
def copy(cars: Seq[CarType]) = new ConcreteGarage(cars)
}
Upvotes: 0
Views: 546
Reputation: 67330
That's why I said beware of the ugliness of having to carry around another type parameter for the rest of your life :)
class ConcreteGarage[C <: Car[C]](val cars: Seq[C]) extends Garage {
type CarType = C
def copy(cars: Seq[C]) = new ConcreteGarage(cars)
}
Of course, its simpler if you have a particular garage.
case class Benzin(litres: Int) extends Fuel
case class Mustang(fuel: Benzin) extends Car[Mustang] {
type FuelType = Benzin
def copy(fuel: Benzin) = Mustang(fuel)
}
case class MustangGarage(cars: Seq[Mustang]) extends Garage {
type CarType = Mustang
def copy(cars: Seq[Mustang]) = MustangGarage(cars)
}
val m = Mustang(Benzin(0))
val g0 = MustangGarage(Seq(m))
val g1 = g0.refuel(m, Benzin(45))
Upvotes: 1