For loops with per-iteration variables
On Mon, Oct 13, 2008 at 9:27 PM, David-Sarah Hopwood <david.hopwood at industrial-designers.co.uk> wrote:
MarkM is right that it just falls out of the natural tail-recursive encoding of a for loop. I got the above expansion wrong by trying to do it imperatively, which was silly -- the tail-recursive expansion is much simpler:
for (let i = initExpr; condExpr; updateExpr) { body } ==> { let ($loop = lambda(i) { if (condExpr) { { body } (updateExpr); $loop(i); } }) { $loop(initExpr); } }
Close. The problem is that updateExpr modifies the same i that's in scope in body, so any closure mentioning i in body captures an i that's modified by the update expression.
David-Sarah Hopwood wrote:
David-Sarah Hopwood wrote:
David-Sarah Hopwood wrote:
Mark S. Miller wrote:
[...] I recommend the per-iteration view. If we can agree quickly on per-iteration, then
for (const x = ...) {...x...}
should be allowed in ES3.1 (whether or not const hoists to block start). After ES3.1
for (const i :T[i] = ...) {...; a[i] = function(){...i...}; ...}
would then mean what it should mean. Cool. Not so fast :-) Consider:
for (let i = 0; i < 10; i++) { ... }
In the "i++", which iteration's 'i' is the LeftHandSideExpression referring to? Or does this expand to something like: [snip] This expansion is wrong for the case where the body updates i. I'll have to think about it some more.
MarkM is right that it just falls out of the natural tail-recursive encoding of a for loop. I got the above expansion wrong by trying to do it imperatively, which was silly -- the tail-recursive expansion is much simpler:
for (let i = initExpr; condExpr; updateExpr) { body } ==> { let ($loop = lambda(i) { if (condExpr) { { body } (updateExpr); $loop(i); } }) { $loop(initExpr); } }
Argh. Still wrong (the updateExpr updates the previous iteration's variable, rather than the next iteration's, which means that a closure that captures the variable in iteration n will end up seeing its value in iteration n+1). To fix that problem it should be:
for (let i = initExpr; condExpr; updateExpr) { body }
==> { let ($loop = lambda(i) { (updateExpr); if (condExpr) { { body } $loop(i); } }, i = initExpr) { if (condExpr) { { body } $loop(i); } } }
This also straightforwardly generalizes to multiple variables.
Yes, it still does.
David-Sarah Hopwood wrote:
MarkM is right that it just falls out of the natural tail-recursive encoding of a for loop. I got the above expansion wrong by trying to do it imperatively, which was silly -- the tail-recursive expansion is much simpler:
for (let i = initExpr; condExpr; updateExpr) { body } ==> { let ($loop = lambda(i) { if (condExpr) { { body } (updateExpr); $loop(i); } }) { $loop(initExpr); } }
or, to handle break and continue properly given an 'escape' construct that works similarly to that in E, something like:
label: for (let i = initExpr; condExpr; updateExpr) { body } ==> { let ($loop = lambda(i) { escape { if (condExpr) { { body } (updateExpr); $loop(i); } } catch ['break', label] {} catch ['continue', label] {(updateExpr); $loop(i);} }) { $loop(initExpr); } }
This also straightforwardly generalizes to multiple variables. I withdraw the contention that it is too magical.