136

I've heard that Scala has path-dependent types. It's something to do with inner-classes but what does this actually mean and why do I care?

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449

1 Answers1

184

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
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 2
    +1 for the answer. I found the last sentence confusing: You say 'type safety that is dependent on values and not types alone'. To me, this sounds like dependent types, but path dependent types don't depend on values per se. Do you think it's confusing as well? – Matthew Farwell Sep 30 '11 at 13:44
  • 4
    @Matthew I understand what you are saying, but path dependent types _do_ depend on values, even if it does not provide the flexibility normally associated with dependent types. – Daniel C. Sobral Sep 30 '11 at 14:19
  • By values, do you mean the value of b1, or the values that are passed to the constructor of b1(i.e 20). I can understand if you mean b1, but I can't see the effect that the 20 has on the code. – Matthew Farwell Sep 30 '11 at 14:37
  • 1
    Exactly, that's what I mean. Initially I read that the type depended upon the values passed to the constructor, not the b1/b2. I understand it now, but it took me a few reads to get it. – Matthew Farwell Sep 30 '11 at 14:50
  • 3
    The easiest explanation is that path-dependent types are just classes with closures, exactly the same way functions can bind variables from the scope. – polkovnikov.ph Dec 09 '14 at 10:34
  • 1
    But maybe there is one fundamental difference to this analogy : one binding takes place at runtime (for closures) and the other binding takes place at compile time (for path dependent types). – jhegedus Nov 04 '17 at 20:34
  • 1
    is it just 'dependent types'? https://en.wikipedia.org/wiki/Dependent_type What does 'path' has anything to do with it? – tribbloid Jan 09 '20 at 00:05
  • 3
    @tribbloid No, not really. A dependent type depends on a _value_, whereas the path dependent types depend on the _path_. In that respect, the example I picked is a bad one, but, hey, it was 10 years ago, and I wasn't trying to distinguish between the two. A better example to get at the distinction might be using an _abstract_ type in a superclass, which will provide you with a guarantee that you'll always pass and receive a proper concrete type. You'll have to look up other resources for this, however. See https://stackoverflow.com/q/24960722/53013, for example. – Daniel C. Sobral Jan 22 '20 at 18:35