Please explain how does the following for desugar and why the last variant does not compile, while the one before it does:
trait A {
def a: Boolean
}
trait B extends A {
def b: Boolean
}
def f() = {
val s = Seq[A]()
val as = for (case x: A <- s if x.a) yield x // this compiles, the result is Seq[A]
val bs = for (case x: B <- s) yield x // this compiles, the result is Seq[B]
for (case x: B <- s) yield x.b // this compiles
for (case x: B <- s if x.b) yield x // this gives an error: Found B => B, Required A => B
}
The behavior is the same with 3.3.7 and 3.7.4, with or without -preview.
It’s explained as a bug.
It should have the same behavior as:
for case (x: B) <- s if x.b yield x
where the extra parens say, “I am a pattern.” More precisely, it signals, “I am refutable.”
Without case, the parens matter:
for x: B <- s if x.b yield x // B => Boolean
for (x: B) <- s if x.b yield x // pattern's type B is more specialized...
But I don’t think parens should matter with case, which always takes a refutable pattern and always filters.
I don’t see a unit test for “parenless case", so I assume it’s a bug.
4 Likes
in the meantime you can write (for (case x: B ← s) yield x).filter(_.b)
That builds the target collection twice; I would advertise the judicious use of parentheses in my reply.
It uses withFilter instead of filter.
1 Like