Question

Given three classes which belong together (I tried to express this through type parameters):

abstract class Row[T <: Table[T, R, B], 
                   R <: Row[T, R, B], 
                   B <: RowBuffer[T, R, B]] extends Immutable {
  def id: Int
  def getTable: T
  def toRowBuffer: B

  def delete() {
    // does not compile: Found Row.this.type ... Row[T, R, B], required: R
    getTable.delete(this)
  }
}

abstract class RowBuffer[T <: Table[T, R, B], 
                         R <: Row[T, R, B], 
                         B <: RowBuffer[T, R, B]] extends Mutable {
  def id: Int
  def getTable: T
  def toRow: R

  def insert(): R = {
    // does not compile: Found RowBuffer.this.type ... RowBuffer[T, R, B], 
    // required: B
    getTable.insert(this)
  }
}

abstract class Table[T <: Table[T, R, B], 
                     R <: Row[T, R, B], 
                     B <: RowBuffer[T, R, B]] {
  def insert(b: B): R = {
    // insert ...
    b.toRow
  }

  def delete(r: R) {
    // delete ...
  }

I have not managed to define the correct types, if there are "cross class" calls. So my questions are:

  • What's the name of this problem?
  • How can I express it in a type-safe way?
Was it helpful?

Solution

I always call this construct "representation" type, but perhaps that is not the official name (you may hear the term "F-bounded quantification"). Your types Row and RowBuffer take a recursive type R and B respectively which describes the type of the final implementation ("representation").

In order to make sure you can fill out the methods of the abstract classes, you need to state that the final implementation will conform to R and B respectively. You do that using a self-type annotation:

abstract class Row[T <: Table[T, R, B], 
                   R <: Row[T, R, B], 
                   B <: RowBuffer[T, R, B]] extends Immutable {
  _: R =>

  ...
}

abstract class RowBuffer[T <: Table[T, R, B], 
                         R <: Row[T, R, B], 
                         B <: RowBuffer[T, R, B]] extends Mutable {
  _: B =>

  ..
}

Note that you can use any identifier, e.g. this: R => or self: R =>.

With these, it compiles, and you cannot create a non-abstract sub class of these which does not satisfy the constraint.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top