drhagen
drhagen

Reputation: 9610

Scala: Making concrete classes of trait with type member that has a representation type

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

Answers (1)

0__
0__

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

Related Questions