I’ve come accros some behaviour of the Scala compiler I have an explanation for of which I’m not certain I’m correct. I’d like to get the propper insight for the behaviour and an explanation, and possible example of unwished behaviour, for the current choices done in the compiler.
The following code compiles and runs like a charm, as expected:
trait T1[A]
{
type ReturnType = this.type
val returnValue : this.type = this
def apply(_x : A) : ReturnType
}
trait T2[A]
extends T1[Option[A]]
{
def apply(_x : A) : ReturnType
}
class C1[A]
extends T1[A]
{
def apply(_x : A) : ReturnType =
{
// some functionality
returnValue
}
}
class C2[A]
extends C1[Option[A]]
with T2[A]
{
def apply(_x : A) : ReturnType = apply(Some(_x))
}
However, changing the type and returnValue lines in e.g.:
type ReturnType = String
val returnValue : String = ""
will result in the following message:
<pastie>:23: error: name clash between defined and inherited member:
def apply(_x: Option[A]): T2.this.ReturnType in trait T1 and
def apply(_x: A): T2.this.ReturnType at line 23
have same type after erasure: (_x: Object)String
def apply(_x : A) : ReturnType
^
<pastie>:38: error: name clash between defined and inherited member:
def apply(_x: Option[A]): C2.this.ReturnType in class C1 and
def apply(_x: A): C2.this.ReturnType at line 38
have same type after erasure: (_x: Object)String
def apply(_x : A) : ReturnType = apply(Some(_x))
^
My explanation is that type checks for this.type are postponed as at the declaration the exact type for this.type is not known. Only the super-type is known.
The two questions I have are:
-
Is my assumption correct?
-
Being as it may that the first code works like a charm, I suspect that the second code would also perform like a charm, but the compiler is too eagerly to do the type check. Why couldn’t it be made that this check is also postponed. My solution now is to duplicate code for my second class where I no longer extends from the first class, but copy the code for the first class and add the extra line.