Define a trait with an abstract class
object Outer { trait X { type T val empty: T }
Now we can make an instance of it:
val x = new X { type T = Int val empty = 42 }
Scala now recognizes x.empty is Int :
def xEmptyIsInt = x.empty: Int
Now define another class
case class Y(x: X) extends X { type T = xT val empty = x.empty }
and enter its instance
val y = Y(x)
But now Scala cannot conclude that y.empty is of type Int . Following
def yEmptyIsInt = y.empty: Int
now produces an error message:
error: type mismatch; found : yxT required: Int y.empty : Int
Why is this so? Is this a scala bug?
We can mitigate this problem by introducing parameters in the case class:
case class Z[U](x: X { type T = U }) extends X { type T = U val empty = x.empty }
Then it works again
val z = Z(x) def zEmptyIsInt: Int = z.empty
But we should always mention all types inside X on the call site site. This should ideally be an implementation detail that leads to the following approach:
case class A[U <: X](z: U) extends X { type T = zT val empty = z.empty }
It also alleviates the problem.
val a = A(x) def aEmptyIsInt: Int = a.empty }
So, in summary, my questions are: why doesn't a simple case work? Is this a valid workaround? What other problems may arise when we follow one of two approaches? Is there a better approach?