Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange (?) for comprehension evaluation in Scala

Now, it took me a while to figure out why my recursion is somehow managing to blow the stack. Here it is, the part causing this problem:

scala> for {
     |   i <- List(1, 2, 3)
     |   j = { println("why am I evaluated?"); 10 } if false
     | } yield (i, j)
why am I evaluated?
why am I evaluated?
why am I evaluated?
res0: List[(Int, Int)] = List()

Isn't this, like, insane? Why at all evaluate j = ... if it ends in if false and so will never be used?

What happens when instead of { println ... } you have a recursive call (and recursion guard instead of if false), I have learned. :<

Why?!

like image 530
Michal Rus Avatar asked Dec 16 '22 04:12

Michal Rus


1 Answers

I'm going to go out on a limb and say the accepted answer could say more.

This is a parser bug.

Guards can immediately follow a generator, but otherwise a semi is required (actual or inferred).

Here is the syntax.

In the following, the line for res4 should not compile.

scala> for (i <- (1 to 5).toList ; j = 2 * i if j > 4) yield j
res4: List[Int] = List(6, 8, 10)

scala> for (i <- (1 to 5).toList ; j = 2 * i ; if j > 4) yield j
res5: List[Int] = List(6, 8, 10)

What happens is that the val def of j gets merged with the i generator to make a new generator of pairs (i,j). Then the guard looks like it just follows the (synthetic) generator.

But the syntax is still wrong. Syntax is our friend! It was our BFF long before the type system.

On the line for res5, it's pretty obvious that the guard does not guard the val def.

Update:

The implementation bug was downgraded (or upgraded, depending on your perspective) to a specification bug.

Checking for this usage, where a guard looks like a trailing if controlling the valdef that precedes it, like in Perl, falls under the purview of your favorite style checker.

like image 70
som-snytt Avatar answered Dec 29 '22 03:12

som-snytt