lexical for-in/for-of loose end

Jason Orendorff jason.orendorff at gmail.com
Fri Feb 3 06:42:26 PST 2012


On Thu, Feb 2, 2012 at 8:08 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
> On Feb 2, 2012, at 5:07 PM, Jason Orendorff wrote:
> > On Thu, Feb 2, 2012 at 5:52 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>
> > wrote:
> > > for (let i=(geti = function() {return i},expr), expr, incr = function(i++),
> > > decr=function(i--), cmp=function(){return i<n}; cmp();incr()) {
> >
> > What?! We should not reward people writing code like that.
>
> No reward, it is simply what the current syntax permits, and it is important
> that semantics are consistently applied.

Oh, I definitely think we should have consistent semantics. It's
difficult, because for(;;) is complicated, but it's worth the effort.
Consider:

    for (let V in EXPR) STMT
    for (let V of EXPR) STMT
    for (let V = EXPR; ...; ...) STMT

I propose that in all three cases, the code that runs once, before the
first iteration, namely EXPR, should be evaluated outside the scope of
the loop variable. STMT should be evaluated within the scope of a
per-iteration binding for V. The same semantics in all three cases.
That seems to me both consistent and sensible.

What is even more important than consistency is that the language work
for people. We know that *not* having per-iteration bindings
astonishes users, because that's how SpiderMonkey does it, and people
are regularly astonished.

> Plus, the desugarings aren't things that are really suitable for teaching
> the semantics to everyday JS programmers.  You instead have to say something
> like:
>
> Ok, this is really complicated but here goes.  For each let/const declared
> in the for header, a fresh variable is located in the loop body for each
> iteration of the loop. However, the values of the loop variables are
> automatically copied from the previous iteration into the next iterations.
>  This means that basic expression operator will work in the loop header
> pretty much like you would expect.  But be careful if you use any function
> expressions in the for header because the loop variables they reference may
> not be from the current iteration and any changes to loop variable they make
> may not have the effect you intended.  But, hey you probably shouldn't do
> those things so it really doesn't matter what it really does.

This "explanation" seems oriented more towards making a rhetorical
point than explaining. If I had to explain this to an everyday Web
programmer, I would just write the three lines of code above and point
out the consistent behavior. That way is simple, and it explains
several things at once.

Anyway, this kind of argument certainly isn't going to win me over,
because the reason I want this change in the first place is so I can
do less explaining! People do write code like this:

    for (let i = 0; i < n; i++) {
        buttons[i] = makeButton();
        buttons[i].click(function () { alert("pushed button " + i); });
    }

It fails to work because in SpiderMonkey the binding isn't
per-iteration. Then I have to explain. Per-iteration bindings mean
less explaining, because fewer people will hit problems. Their code
will just work.

-j


More information about the es-discuss mailing list