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

724 Views Asked by At

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.

2

There are 2 best solutions below

0
On BEST ANSWER

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

0
On

Interesting question. Here's a simplified example:

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
}

Note three things here:

1.Changing self type annotation to inheritance makes everything work out:

trait Component extends Base {
  override type T <: MyT
  val t: T
  def yyy = t.xxx
}

2.Overriding methods from self-type is not problematic:

trait Base { def foo }

trait Component { self: Base =>
  override def foo = println("foo")
}

3.If instead of narrowing type T we make it concrete, then the overriding works:

trait Base { type T }

trait Component { self: Base =>
  override type T = List[Int]
  val t: T
  def yyy = t.reverse
}

So, my formulation of the question would be - how come that in case of inheritance all three cases work just fine (1. overriding method, 2. overriding abstract type member by narrowing, 3. overriding abstract type member by making it concrete), but in case of self types second case is problematic, while other two work fine?

Consider editing your question (removing ComponentImpl, Factory etc.) to simplify it for other readers.