3

I have two traits, each with a type parameter for one of its members. In the first trait, I have a function that takes an instance of the second trait and an instance of the second trait's type member. This function calls a function in the second trait that is expecting that instance of its type member. However, I cannot figure out how to properly parameterize the call so that this actually works. Here is a simplified example that fails:

trait Garage {
  type CarType <: Car
  def Cars: Seq[CarType]

  def copy(Cars: Seq[CarType]): Garage

  def Refuel(car: CarType, fuel: CarType#FuelType): Garage = {
    val car_index = Cars.zipWithIndex.find(_._1 == car).get._2

    copy(Cars.updated(car_index, car.Refuel(fuel)))
  }
}

trait Car {
  type FuelType <: Fuel
  def Fuel: FuelType

  def copy(Fuel: FuelType): Car

  def Refuel(fuel: FuelType): Car = {
    copy(fuel)
  }
}

trait Fuel

This fails with the following error:

error: type mismatch;
 found   : fuel.type (with underlying type Garage.this.CarType#FuelType)
 required: car.FuelType
    copy(Cars.updated(car_index, car.Refuel(fuel)))
                                            ^

How do I constrain the Garage.Refuel function so that it accepts a Car and any Fuel that is acceptable to that type of Car?

virtualeyes
  • 11,147
  • 6
  • 56
  • 91
drhagen
  • 8,331
  • 8
  • 53
  • 82

2 Answers2

7

Try:

def Refuel(car: CarType)(fuel: car.FuelType): Garage = {
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • I was afraid of this answer. But I suppose it's OK if I don't have to make too many calls to the function; otherwise, the extra parentheses get annoying. – drhagen Jun 28 '12 at 13:53
  • I took Sciss's word for it that this would work, but it turns out that in Scala 2.9.2, anyway, it does not compile and gives the following errors, which don't actually make must sense: `error: illegal dependent method type; def Refuel(^car: CarType)(fuel: car.FuelType): Garage = { ;; error: type mismatch; found : Seq[this.Car]; required: Seq[Garage.this.CarType]; copy(Cars.updated^(car_index, car.Refuel(fuel)));` (caret-arrows inserted directly into line) – drhagen Jun 28 '12 at 22:33
  • @drhagen You need to enable dependent method types with a compiler flag. – Daniel C. Sobral Jun 29 '12 at 01:34
4

Although Daniel's answer works, I would like to point out an alternative, which is kind of my own panacea. I had been struggling a lot with getting path dependent types right, and ended up with the following strategy. It's a bit more 'ugly' as you now need to write an additional type parameter, but this approach has never let me down:

trait Garage {
  type CarType <: Car[CarType]  // CarType appears as representation type on the right
  def cars: Seq[CarType]

  def copy(Cars: Seq[CarType]): Garage

  def refuel(car: CarType, fuel: CarType#FuelType): Garage = copy(
    cars.map {  // map is more concise for what you try to achieve
      case `car` => car.refuel(fuel)  // backticks to find the particular car
      case other => other
    })
}

trait Car[C <: Car[C]] {  // add a 'representation type'
  type FuelType <: Fuel
  def fuel: FuelType

  // use 'C' instead of 'Car' everywhere, and qualify the type member with 'C#'
  def copy(fuel: C#FuelType): C

  def refuel(fuel: C#FuelType): C = copy(fuel)
}

trait Fuel

I don't know if this 'representation type' concept has a formal name (I would be interested to know). I tried to look up who taught me this, but didn't find it (at least in stackoverflow).

0__
  • 66,707
  • 21
  • 171
  • 266
  • That does indeed work. I'm trying to figure out what that even does to the trait. I continue to be amazed at the problems that can fixed in Scala simply by sticking a bounded type parameter in the front of something and then saying that you can return that type. – drhagen Jun 28 '12 at 14:02
  • Also, that is a far superior way to replace a given element of an array. – drhagen Jun 28 '12 at 14:05
  • 4
    When you have that kind of recursion in a constraint it's called [_F_-bounded polymorphism](http://en.wikipedia.org/wiki/Bounded_quantification). – Travis Brown Jun 29 '12 at 14:07