What is meant by Scala’s path-dependent types?

My favorite example:

case class Board(length: Int, height: Int) {
  case class Coordinate(x: Int, y: Int) { 
    require(0 <= x && x < length && 0 <= y && y < height) 
  }
  val occupied = scala.collection.mutable.Set[Coordinate]()
}

val b1 = Board(20, 20)
val b2 = Board(30, 30)
val c1 = b1.Coordinate(15, 15)
val c2 = b2.Coordinate(25, 25)
b1.occupied += c1
b2.occupied += c2
// Next line doesn't compile
b1.occupied += c2

So, the type of Coordinate is dependent on the instance of Board from which it was instantiated. There are all sort of things that can be accomplished with this, giving a sort of type safety that is dependent on values and not types alone.

This might sound like dependent types, but it is more limited. For example, the type of occupied is dependent on the value of Board. Above, the last line doesn’t work because the type of c2 is b2.Coordinate, while occupied‘s type is Set[b1.Coordinate]. Note that one can use another identifier with the same type of b1, so it is not the identifier b1 that is associated with the type. For example, the following works:

val b3: b1.type = b1
val c3 = b3.Coordinate(10, 10)
b1.occupied += c3

Leave a Comment