scalaabstract-typeself-type

Abstract types + self types + type overriding, and error "Value xxx is not a member of Component.this.T"


The error is illustrated in the following:

trait Base { type T }

trait Component { self: Base =>
  override type T <: MyT

  val factory: Factory 

  trait Factory { def get_t: T }

  trait MyT { def xxx: Unit }

  class User {
    val t: T = factory.get_t

    def yyy = t.xxx
  }
}

trait ComponentImpl { self: Component => 
  type T = TImpl

  class TImpl extends MyT {
    def xxx = println("xxx")
  }

  val factory = new Factory {
    def get_t: T = new TImpl
  }
}

And I get the error:

<console>:26: error: value xxx is not a member of Component.this.T
       def yyy = t.xxx

With some redundancy, I also post the minimal example as suggested by @slouc and @Kolmar

trait Base { type T }

trait MyT { def xxx: Unit }

trait Component { self: Base =>
  override type T <: MyT
  val t: T
  def yyy = t.xxx // error
} 

It seems that I cannot leverage on the incremental, partial knowledge given by the constraint.

My question is not about the approach, rather I am interested in understanding the reasons behind the error.

To provide more context, I would also add that I got into this problem when trying to convert an inheritance relationship into a self-type-based relationship between two components.


Solution

  • With the self type annotation you are basically telling the compiler that the self-reference has a different type

    self: Base
    

    If you would do this with any normal expression, the only type self has now, is Base. Somehow type annotations are handled as a special case and maintain their original type as well, the type we actually seem to get is

    self: Base with Component
    

    Now, somehow if you refer to Component.this it seems to loose this information in case of overwritten type members (or maybe the overwriting is just the wrong way around, but I cannot see any explanation that this is not a bug)

    Now if you actually annotate this type the problem goes away:

    trait Component { self: Base with Component => ... }
    

    Which also clearly shows this is a bug because this works fine:

    trait Component { self: Base =>
      val v:  Base with Component = self
    }
    

    This is actually a known Bug:

    https://issues.scala-lang.org/browse/SI-7255?jql=text%20~%20%22override%20self%20type%20abstract%22