scalacrtpabstract-type

Curiously recurring template pattern constraints with Scala abstract types


I have a Scala compiler error puzzle that I can't figure out. For reasons not given here, I need to declare my classes using the curiously recurring template pattern. Some of my classes need to contain others as parameters. For example, a stripped down version of my code looks like this:

trait Container[C <: Container[C]] {
  def stuff: C
}

trait Aggregation {
  def fill(x: Double): Unit
}

class Counted extends Container[Counted] {
  def stuff = this
}

class Counting extends Container[Counting] with Aggregation {
  def stuff = this
  def fill(x: Double) { }
}

class Binned[V <: Container[V]](v: V) extends Container[Binned[V]] {
  def stuff = this
  def substuff = v
}

class Binning[V <: Container[V] with Aggregation](v: V) extends Container[Binning[V]] with Aggregation {
  def stuff = this
  def substuff = v
  def fill(x: Double) { }
}

The above works fine. Now I want to add an abstract type from each -ing to each -ed:

trait Container[C <: Container[C]] {
  type Ed <: Container[Ed]
  def stuff: C
}

trait Aggregation {
  def fill(x: Double): Unit
}

class Counted extends Container[Counted] {
  type Ed = Counted
  def stuff = this
}

class Counting extends Container[Counting] with Aggregation {
  type Ed = Counted
  def stuff = this
  def fill(x: Double) { }
}

class Binned[V <: Container[V]](v: V) extends Container[Binned[V]] {
  type Ed = Binned[V]
  def stuff = this
  def substuff = v
}

class Binning[V <: Container[V] with Aggregation](v: V) extends Container[Binning[V]] with Aggregation {
  type Ed = Binned[V#Ed]
  def stuff = this
  def substuff = v
  def fill(x: Double) { }
}

and the compiler has the nerve to tell me

<console>:34: error: type arguments [V#Ed] do not conform to class Binned's type parameter bounds [V <: Container[V]]
             type Ed = Binned[V#Ed]

when Ed is clearly <: Container[Ed] for all Containers.

Strictly speaking, type Ed is only needed on Containers with Aggregation, and if I move it there, I get the same error.

Does anybody know how I can state my intention to the compiler?


Solution

  • class Binning[V <: Container[V] with Aggregation](val v: V) extends Container[Binning[V]] with Aggregation {
      type Ed = Binned[v.Ed]
      def stuff = this
      def substuff = v
      def fill(x: Double) { }
    }
    

    works. But it does seem to me like your version should compile as well.