Search code examples
scalatraitsabstract-type

Overriding abstract type does not work with control abstraction


This question follows the one in Cake pattern with overriding abstract type don't work with Upper Type Bounds. I want to override the abstract type in trait with <:. The previous links gives the solution which consists in changing the order of linearization by writting this: Cake with S in trait S. However, I added a control abstraction named control in the following code. I want to call the method t in it.

trait A {
  def ping = println("ping")
}

trait Cake {
  type T
}

trait S { this: Cake with S =>
  type T <: A with S
  def t: T
  def s = println("test")
  // def control(c: =>T): T = c // compile
  // def control(c: =>T): T = c.s // does not compile
  def control(c: =>T): T = c.t // does not compile
  t.ping
  t.s
}

But, this code results in a compilation error that I can't explain

 found   : S.this.T#T
 required: S.this.T
         def control(c: =>T): T = c.t
                                    ^

What is wrong ?


Solution

  • def control(c: =>T): T
    

    Has return type S#T. Clearly, c has type T as well. Hence, returning c in:

    def control(c: =>T): T = c
    

    compiles, while returning c.s does not compile because s has return type of Unit. c.t does not compile because it has return type of S#T#T.

    Thus, the following will compile:

    def control(c: =>T): T#T = c.t
    

    As the specifics behind everything that is happening here are a bit non-trivial in general, I suggest you google "type projection" and "path dependent types" for more information.

    Essentially what is happening is c has type T, where T <: A with S, so we know T must have all of S's members, one of which is defined as def t: T. But this T is not necessarily the same as our T, so it does not conform when we call c.t in your example. What we want is the T of type T (or equivalently, T's T, or the T of our type member T). Here T#T indicates exactly what we want.