That would be unreasonable. That is why, with a simplified example. We donβt need two common parameters, we donβt need the subtypes TI, TJ and TK either
trait TA[X <: TA[X]] trait TC[X <: TA[X]] class Z extends TC[TA[_]]
arguments of type [TA [_]] do not match the parameter of type TC of the border [X <: TA [X]]
Let us see why this expression is unfounded, and it is correct that it fails. Add code. I change TC to class so that the code can be translated into java. A trait will also work in scala.
trait TA[X <: TA[X]] {def f(x: X) } class TC[X <: TA[X]] {def g(x: X) = xf(x)}
It works fine, x: X also TA[X] , so there is a subroutine f that takes X
If we try instead
class TC2[X <: TA[_]] {def g(x: X) = xf(x)}
then he fails. We know that there is an f method X , but we donβt know what type is needed as an argument, we cannot know that X will be fine. Indeed, suppose that
class T1 extends TA[T1]] {def f(t1: T1) = {}; def t1Only = println("only in T1")} class T2 extends TA[T1]] {def f(t1: T1) = t1.t1Only }
Now, if TC2 was allowed, I could create TC2[T2] , call g with T2 , which will call f in T2 with T2 . This is unacceptable, and correct, since T2 does not have the t1Only method.
This shows why TC cannot accept TA[_]] as its parameter, since it would allow T2 , which is incompatible with method g . So why can't we define Z with the parameter TA[_] . That would be exactly the same in java and with your source code.
Change I'm a little to blame for my answer. For the reason I gave why this should not be resolved, I thought there would be an easy way around the problem. This failed, I did not have time for further investigation and posted it without mention. The workaround was on its own. If we do
trait TA[X <: TA[X]] {self: X => }
then we cannot determine T2 extends TA[T1] . So there is more limited source code. But we should still accept restrictions on the source code, because it was unreasonable. Thus, this cannot be just a syntactic trick; it must make things impossible, which are not. I thought that the T2 extends TA[T1] was probably not what was intended, and that this was the only thing to prevent.
Apparently, this was not the same mistake. Now I have no example why this should not work. Which, of course, does not mean that they are not.
Then I looked at Miles' solution, wondering why the ability of T2 extends TA[T1] would not hurt him. So, dropping TB , Y , TI , TJ and TK :
trait TA{type X} trait TC{self => type X <: TA{type X <: self.X} class Z extends TC{type X = TA}
It compiles. And we can do
trait T1 extends TA{type X = T1} trait T2 extends TA{type X = T1}
But we can do nothing:
trait TA {type X <: TA; def f(x: X)} trait TC {self => type X <: TA{type X <: self.X} def g(x: X) = xf(x) }
We get the following error on g , for argument X f
type mismatch; found: x.type (with base type TC.this.X) required: xX
TC not original (as originally, g allowed). This is because TA has type X <: self.X , and TC[X <: TA[X]] is stronger than type X = self.X If we write that instead we return to the original error, Z does not compile. So, this TC somewhat different from the original TC ( type X = self.X ) and TC2 (without knowledge of X of TA). Again, a restriction on the source code, we cannot define g .
If the limit is acceptable, you're fine. I do not know how to write it as a generic one (and how to write a type self {self : X => with an abstract type member). Miles is definitely an expert, Iβm sure that he will be able to tell how this is done or that this is not possible.