lexical for-in/for-of loose end

Andreas Rossberg rossberg at google.com
Tue Feb 7 10:00:50 PST 2012


On 7 February 2012 15:40, Grant Husbands <esdiscuss at grant.x43.net> wrote:
> On 7 February 2012 13:32, Andreas Rossberg <rossberg at google.com> wrote:
>> What should the following do?
>> let a = [], b = [], c = [], d = []
>> for (int i = 0, f = function(){ return function() { ++i } }; i < 10;
>> d[i] = function() { return i }, ++i) {
>>  a[i] = f;
>>  b[i] = f();
>>  c[i] = function(){ return i }
>> }
>> print(c[2]()); print(d[2]())
>> a[4]()(); print(c[4]()); print(d[4]())
>> b[7](); print(c[7]()); print(d[7]())
>
> Here's what it does under the proposed scheme:
> c[2]() -> 2
> d[2]() -> 3
> a[4]()() increments the i that is 10 to 11.
> c[4]() -> 4
> d[4]() -> 5
> b[7]() increments the i that is now 11 to 12.
> c[7]() -> 7
> d[7]() -> 8
> Basically, the closures lexically within the loop init always see the
> latest version of i. Those lexically within the rest of the loop
> always see the i that was available at the end of their iteration. The
> iteration part of the loop is considered to be at the start. A related
> gotcha is that d[9]() would return 12, if added to the end of that
> sequence of calls. (I'm answering to the best of my ability, not
> trying to defend anything.)

But note that the environment in b's closures is not the for-loop
environment. Instead, it is a local environment, whose parent is the
for-loop environment. To make that case work, it is not enough to be
able to modify the [[Scope]] environment of closures -- in general,
you'd need to swap out arbitrary parts of an environment chain.


>> Allow me to be blunt: this is literally raping the concepts of
>> declarative environment and lexical closure. It is a hack, completely
>> non-orthogonal,
>
> I think that may be more than blunt; it is strongly emotive and also
> vague enough that it can't really be answered. However, I and others
> do share the concern that it may introduce too much complexity, and
> then only really add support for a very rare usage.

You are right, I should have been more careful. I am sorry about that.

This is not merely a question of complexity, though. It's more
fundamental. Environments are immutable mappings from names to
locations -- that's a basic axiom of lexical scoping. Breaking it will
have unforeseeable consequences. That may be vague, I agree, but I
prefer not to find out concretely. :)  Subtle combinations of
higher-orderness and state rarely let you down in terms of nasty
surprises.


>> Seriously, before we consider going there, I'd rather have a step on
>> the break and stick to C-style for-semantics.
>
> I think we wouldn't need to go that far; your generalization of Mark's
> desugaring covers plenty of use cases and certainly the one of most
> common loop/closure gotchas. To my understanding, that's the favoured
> idea, so far, and we're just exploring this one to see where it might
> lead.

I am fine with either. So far I have favoured
fresh-variables-per-iteration, but the current discussion has raised
some doubts in the back of my mind.

/Andreas


More information about the es-discuss mailing list