return when desugaring to closures

Jon Zeppieri jaz at
Mon Oct 13 17:00:26 PDT 2008

On Mon, Oct 13, 2008 at 7:39 PM, Brendan Eich <brendan at> wrote:
> On Oct 13, 2008, at 4:14 PM, Jon Zeppieri wrote:
>> Yes, and binding a fresh induction variable on every iteration makes
>> sense for a 'for-each' loop (as in the bug report you cited), where
>> the user is not in charge of updating the induction variable by means
>> of explicit assignment.  In a plain 'for' loop, however, it *is* magic
>> if an assignment results in a fresh binding.
> Why is the assignment operator relevant? The question is the binding
> scope of i in
>   for (let i = 0; i < N; i++) ...

How is scope the issue?  As far as I know, we don't disagree about scope.

The assignment I'm referring to, in this example, is the 'i++' part.
Mark is proposing that this does not mean "increment i by one," but
rather "rebind i with the value of i+1" -- which is completely
different and not what the user wrote.

> No curly braces required, we already have this in JS1.7+ and the let
> is scoped to the for head except for the initializer of i (you can
> write let i = x, j = y; too -- x and y are evaluated in the outer
> scope). There's scope magic in this form even though it uses = for
> assignment and creates only one block scope for the loop, no how many
> times the loop iterates.

I think you misread my message.  We do not disagree at all about the
scope of the initialization clause, but rather the meaning of the
update clause.

This is why the 'for-each' loop is not problematic; it doesn't have an
update clause.

>>  And it's unexpected magic.
> Users differ on this point, but we've had long-standing confusion and
> complaints about closures capturing the last value of i in
>   let a = [1,2,3,4];
>   let b = [];
>   for (let i = 0; i < a.length; i++)
>     b[i] = function () { return i*i; }
> and the like. Getting 16 back from b[0]() is unexpected bad magic.

I understand this, but I don't see how the answer is to change the
meaning of 'i++' when used in the update clause of a 'for' loop.

> Users may be modeling closures as capturing bindings, not scope chains
> of mutable objects, one per for (let...) statement or explicitly
> braced block. If so, could we make let declaration capture this way?
> Again, I'm proceeding from real users' complaints, not idle wishes.
>> Mark said that there was a desugaring for 'for' to 'lambda,' without
>> special cases, where this all works out, but I haven't been able to
>> figure out what rewrite he had in mind.
> Tail-recursive lambda rewrite of a C-style for loop should be easy for
> you :-P.

That's not the point.  I'm talking about a rewrite from 'for' to
'lambda' that satisfies the following properties:

1) for (var i = 0; i < len; i++) ... continues to mean what it means in ES3.
2) for (let i = 0; i < len; i++) ... has the proper scope for 'i'
(which you reiterated above), *and* 'i' is rebound -- not mutated --
on each iteration.
3) The rewrite rules are the *same,* regardless of whether it's a "for
(var ...)" or a "for (let ...)" loop.

At least, that's what I took Mark to mean.  He can correct me if I'm wrong.


More information about the Es-discuss mailing list