scalaabstract-typetype-memberstype-projection

Referring abstract type member of a type parameter


My scenario is like:

trait A {
    type B
    def foo(b: B)
}

trait C[D <: A] {
    val d: D
    def createB(): D#B
    def bar() {
        d.foo(createB)
    }
}

In REPL, it complains

<console>:24: error: type mismatch;
 found   : D#B
 required: C.this.d.B
       a.bar(createB())

What's wrong with this ? And (if possible at all) how to correct this code ?


Solution

  • D#B is a type projection, and is not the same as d.B. You have a type mismatch because in foo, Bactually meant this.B, which as said is not the same as D#B (the latter being more general). Informally, you can think of D#Bas representing any possible type that the abstract type B can take for any instance of D, while d.B is the type of B for the specific instance d.

    See What does the `#` operator mean in Scala? and What is meant by Scala's path-dependent types? for some context.

    One way to make it compile it is by changing createB's return type to d.B:

    def createB(): d.B
    

    However in many cases such a solution is too restrictive because you are tied to the specific instance d, which might not be what you had in mind. Another solution is then to replace the abstract type with a type parameter (though it is more verbose):

    trait A[B] {
      def foo(b: B)
    }
    
    trait C[B, D <: A[B]] {
      val d: D
      def createB(): B
      def bar() {
        d.foo(createB)
      }
    }