It looks like you ran into one of those corner cases where Scala does not have an elegant solution.
What you need in the place of Expression[?]
as the return type is actually a type that represents "this type", being the type that the trait is actually mixed into. There is no shorthand in Scala that lets you express this in a simple way, and you cannot use this.type
either, as that is too specific and can only be used when you really always return this
.
The solution usually quickly introduces mind-bending boilerplate. The Scala collections for example suffer from the same issue, traits like MapLike
need to encode similar things.
I tried to quickly modify your sample to encode such a type. I made sure it compiles, but I did not run it:
sealed trait Expression[A, ThisType <: Expression[A, ThisType]] {
def replace[B,C <: Expression[B,C]](a: Expression[B,C], b: Expression[B,C]): ThisType
}
trait Variable[A] extends Expression[A, Variable[A]] {
def replace[B,C <: Expression[B,C]](a: Expression[B,C], b: Expression[B,C]) =
if (a == this) b.asInstanceOf[Variable[A]] else this
}
case class Sequence[A <: Expression[_,A]](values: Seq[A]) extends Expression[A, Sequence[A]] {
def replace[B,C <: Expression[B,C]](a: Expression[B,C], b: Expression[B,C]) =
if (a == this) b.asInstanceOf[Sequence[A]]
else Sequence(values.map(_.replace(a, b)))
}
It even has to use a cast, which usually is a smell on its own, but I'm not sure there is a way to avoid it in this case. At least it is safe, we know it has to have the right type, just the compiler does not know it. :-)