Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: illegal inheritance; self-type Y does not conform to X's selftype SELF

I have a trait, which takes a type parameter, and I want to say that the objects that implements this trait will also conform to this type parameter (using generics, for Java's compatibility)

The following code:

trait HandleOwner[SELF <: HandleOwner[SELF]] {
self : SELF =>
    // ...
    def handle: Handle[SELF]
}

trait Common[SELF <: Common[SELF]]  extends HandleOwner[SELF] {
    // ...
}

Gives me the following error:

illegal inheritance;  self-type test.Common[SELF] does not conform to
test.HandleOwner[SELF]'s selftype SELF

If I change Common to:

trait Common[SELF <: Common[SELF]]  extends HandleOwner[SELF] {
self : SELF =>
    // ...
}

Then the error goes away.

Why is it that I have to repeat the same declaration in every non-concrete type. If I would have a base class, and say "extends Comparable", I don't have to repeat "extends Comparable" in every derived type, as long as the concrete classes implement the compareTo method. I think it should be the same thing here. I am just saying that a type extending HandleOwner will be also a SELF, and the compiler should just accept it, and take it into consideration while not requiring every non-concrete subtype to repeat the same thing again.

A am doing this to avoid having to use a class-cast, but I will be literally extending every class from this trait, and I don't see that I should have to repeat this declarations hundreds or even thousands of times!

like image 305
Sebastien Diot Avatar asked Sep 11 '11 14:09

Sebastien Diot


1 Answers

Self type is more akin to generic constraint than to inheritance. With class C[A <: B], the constraint must be repeated all along in subclasses : class D[A <: B] extends C[A]. The constraint must be repeated until it is satisfied, that is until you have chosen an actual parameter type which indeed satisfies <: B. Same for the self type. Writing self: A => does not makes your type extend A. It ensures that it will ultimately have to be mixed in with A before it is actually instantiated.

On the opposite, when you extend Comparable, you have made your class a Comparable, not put a constraint for later on. But the fact that you need to implement compareTo has still to be repeated all along with abstract until you actually implement it.

Certainly the compiler could do without repeating <: B, self: A =>, and abstract, the info is available to it. This is language designer choice. At least, having to repeat self: A => is not different from the rules everywhere else.

like image 156
Didier Dupont Avatar answered Oct 21 '22 03:10

Didier Dupont