OK, so the point is not “the workaround is ugly”, but “the restriction is arbitrary”. Makes sense to me. In fact, the restriction complicates defining and implementing the desugaring.
Inferring the monad to use, and then call pure
, doesn’t work with the current stdlib API—you’d need to change the API to do that. Which probably would be best here. If you don’t want a type-directed desugaring (and you don’t, for consistency), call a method pure(x)
(or some other name) and let users shadow it.
However, value definitions and using pure
don’t have the same behavior. I’ll get back to that.
In fact, the restriction seems to complicate the spec and the compiler. They both rewrite p <- e; p' = e'
to
(p, p′) <- for (x@p <- e) yield { val x′@p′ = e′; (x, x′) }
The compiler also handles up to 21 value definitions.
Instead, if you try harder to use structural recursion, you can replace that rule by adding:
for {} yield e => e
// feel free to forbid this in source Scala if you dislike it, but it’s an arbitrary restriction
for { p = e; generators } yield e => val p = e; for { generators } yield e
As a side effect, the new rules allow p = e
at the top. I expect they’re not fully equivalent in corner cases (like if patterns use side-effectful extractors, which is however “evil”), but they might be good enough.
A bigger potential problem is: how do you handle p = e
if p
is refutable? Because the above scheme gives a match failure, unlike p <- pure(e)
. I semi-remember complaints here.
But the current translation fails there too—that matches the spec, in fact, narrowly. If you applied the rewrite rule before inserting filter calls for refutable patterns (against the current spec), you’d have a behavior that:
- handles refutable patterns in value definitions
p = e
- but makes it much harder/impossible to have value definitions first, unless you use the
pure
-based desugaring.
- in turn, using
pure
has a performance cost unless you have some optimizer (I think it needn’t be smart. pure
isn’t a virtual call.) On the other hand, creating tuples isn’t free either.
Right now, we seem to have the worst of both worlds—value definitions are inconsistent with generator regarding refutability, and they can’t come first.
scala> for {x <- List(()); (a, b) = List[Any](1): Any} yield 2
scala.MatchError: List(1) (of class scala.collection.immutable.$colon$colon)
at .$anonfun$res4$1(<console>:12)
at scala.collection.immutable.List.map(List.scala:272)
... 29 elided
scala> for {x <- List(()); (a, b) <- List(List[Any](1): Any)} yield 2
res5: List[Int] = List()