Path-dependency and inner classes

Viewed 135

Following sample code fails to compile with the error listed below.

class Outer {
  class Inner

  val instance: Inner = new Inner

  def verify(pd: Inner): Boolean = pd == instance
}

class UseOuter(val pdp: Outer) {
  def foo: pdp.Inner = pdp.instance
}

class UseCase {
  val pdp = new Outer
  val user = new UseOuter(pdp)

  val i = user.foo

  pdp.verify(i)
}

Error:

test.sc:19: type mismatch;
 found   : UseCase.this.user.pdp.Inner
 required: UseCase.this.pdp.Inner
  pdp.verify(i)
             ^
Compilation Failed

I'm not entirely sure but reading Inner Classes suggests that it's the intended behaviour? Specifically the following sentence:

As opposed to Java-like languages where such inner classes are members of the enclosing class, in Scala such inner classes are bound to the outer object.

If that's the case and this is indeed the desired behaviour, is there a way to encode this requirement in Scala?

There are a few similar questions but they all concern inner types, and not classes and the proposed solutions will not apply.

2 Answers

Here's an approach that maintains the safety of path-dependent types for those times when you want it. First, modify UseOuter to take the type of pdp as a type parameter:

class UseOuter[O <: Outer](val pdp: O) {
  def foo: pdp.Inner = pdp.instance
}

Then, when you instantiate UseOuter, explicitly pass pdp.type as the type parameter:

val user = new UseOuter[pdp.type](pdp)

This allows the compiler to remember that the pdp in UseOuter is the same pdp in UseCase, so the types match up correctly.

For example you can use type projections:

class Outer {
  class Inner

  val instance: Inner = new Inner

//  def verify(pd: Inner): Boolean = pd == instance
  def verify(pd: Outer#Inner): Boolean = pd == instance
}

class UseOuter(val pdp: Outer) {
//  def foo: pdp.Inner = pdp.instance
  def foo: Outer#Inner = pdp.instance
}

class UseCase {
  val pdp: Outer = new Outer
  val user: UseOuter = new UseOuter(pdp)

//  val i: user.pdp.Inner = user.foo
  val i: Outer#Inner = user.foo

  pdp.verify(i)
}
Related