Desugaring "for" and "let"

# Axel Rauschmayer (14 years ago)

harmony:let

Given the following code: var a = []; for (let i = 0; i < 3; i++) { a.push(function () { return i; }); } print(a0); // 0 or 3?

I would expect this “desugaring”: var a = []; (function() { var i; for (i = 0; i < 3; i++) { (function() { a.push(function () { return i; }); }()); } }()); print(a0); // 3

I remember that this has been previously discussed, but can't find the thread. For me, the above desugaring is easier to understand than the lambda coding.

Compare: With Java, you have to declare final int finalI = i; inside a for loop if you want to refer to i from an inner class. Similarly, if you want the result "0", you would need to write:

var a = [];
(function() {
    var i;
    for (i = 0; i < 3; i++) {
        (function() {
            var iCopy = i;
            a.push(function () { return iCopy; });
        }());
    }
}());
print(a[0]()); // 0

Axel

# Brendan Eich (14 years ago)

On Jul 11, 2011, at 7:18 AM, Axel Rauschmayer wrote:

I remember that this has been previously discussed, but can't find the thread. For me, the above desugaring is easier to understand than the lambda coding.

Just recently:

esdiscuss/2011-June/015615 (thread head)

and previously:

esdiscuss/2008-October/007826

Compare: With Java, you have to declare final int finalI = i; inside a for loop if you want to refer to i from an inner class.

Yeah, but that sucks so let's not cite it as any kind of precedent :-|.

Anyway, for-in and for-of with let give fresh binding per iteration -- that's the plan for ES.next.

Ye olde C-style for(init; cond; update) {...} loops are thin veneer for init; while (cond) {... update}, and as Jon Z. pointed out, since you can take them apart accordingly, the binding semantics should not shift as you do.

# Axel Rauschmayer (14 years ago)

Just recently:

esdiscuss/2011-June/015615 (thread head)

and previously:

esdiscuss/2008-October/007826

Thanks!

Compare: With Java, you have to declare final int finalI = i; inside a for loop if you want to refer to i from an inner class.

Yeah, but that sucks so let's not cite it as any kind of precedent :-|.

Anyway, for-in and for-of with let give fresh binding per iteration -- that's the plan for ES.next.

Ye olde C-style for(init; cond; update) {...} loops are thin veneer for init; while (cond) {... update}, and as Jon Z. pointed out, since you can take them apart accordingly, the binding semantics should not shift as you do.

==== Example ====

for(let i=0; i < arr.length; i++) { // ... }

==== Desugars to ====

let i=0; while(i < arr.length) { // ... i++; }

As per "Jon’s criticism", I would expect every iteration to create a new environment whose outer environment is always the same – the environment where "i" is bound (right?). That is: "i" is stored outside the environments that are created by repeatedly "invoking" the loop body.

Furthermore, I always thought that there was an additional block wrapped around a for loop with a let, because once you leave the loop, "i" usually ceases to exist.

==== Additional block ====

{ let i=0; while(i < arr.length) { // ... i++; } }

One also has to consider the case where a loop is used to increment a non-local variable. That is, loops should be allowed to have side effects. I’m not sure how this applies to iterators, as they are handled by reference.

# Brendan Eich (14 years ago)

On Jul 11, 2011, at 2:54 PM, Axel Rauschmayer wrote:

As per "Jon’s criticism", I would expect every iteration to create a new environment whose outer environment is always the same – the environment where "i" is bound (right?). That is: "i" is stored outside the environments that are created by repeatedly "invoking" the loop body.

Yes, there's an implicit block snugly wrapping the for(let...;...;...){...} loop to hold the let's bindings.