I have a simple case to test the type inference capability of scala:
trait Super1[S] {
final type Out = this.type
final val out: Out = this
}
trait Super2[S] extends Super1[S] {
final type SS = S
}
case class A[S](k: S) extends Super2[S] {}
val a = A("abc")
implicitly[a.type =:= a.out.type]
// success
implicitly[a.Out =:= a.out.Out]
// success
implicitly[a.SS =:= a.out.SS]
implicitly[a.SS <:< a.out.SS]
implicitly[a.out.SS <:< a.SS]
// oops
The error for the last 4 lines looks like this:
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:28: Cannot prove that a.SS =:= Super2.this.SS.
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:29: Cannot prove that a.SS <:< Super2.this.SS.
[Error] /home/peng/git-spike/scalaspike/common/src/test/scala/com/tribbloids/spike/scala_spike/AbstractType/InferInheritance.scala:30: Cannot prove that Super2.this.SS <:< a.SS.
three errors found
Clearly scala compiler screwed up on these cases: if I move:
final type Out = this.type
final val out: Out = this
to be under Super2 it will compile successfully. My question is that: why the status quo inference algorithm won't work in this case? and how can I rewrite my code to circumvent this problem of the compiler?
If
a.SS =:= a.out.SSthen we should be able to use one in place of the other. Is that the case?No.
Compiling this I get:
It looks like Scala understands that
a.SSis reallyString, which isn't surprising.But clearly
a.out.SSisn'tStringbut rather...S.Strangely this works even though it's clearly wrong:
If you just define
outto be of typethis.typethen your code works.The best I can come up with is that when defining the type of
out, Scala fails to associate the actual type represented bySwithoutand so the typeSSas viewed throughoutjust has the generic typeS.The type
Outdoes seem to understand the actual type represented byS, so this works:and this correctly fails to compile: