lexical for-in/for-of loose end

Brendan Eich brendan at mozilla.org
Mon Feb 6 11:21:13 PST 2012


Grant Husbands wrote:
>>   But this is all beyond the spec.
>
> I don't think it is. What Herby's idea and these formulations present
> is a way for let-based loops to have modifications in closures
> captured in the for-head that alter the loop variables in a way that's
> visible to the current loop iteration.

And read back changes from the current iteration.

Yes, I see that a new (novel!) observable semantic model is being 
proposed here, not an optimization. At first it seemed Herby was 
proposing just the usual optimizations.

I still do not think it's wise to specify in terms of such 
pointer-updating reference semantics, not for the body closures that 
want to capture loop control variables. But only if closures in the 
for-head capture loop variables? That would be Allen's "DWIM" (Do What I 
mean) semantics.

DWIM is tempting. Perl is full of it and it can be convenient at first. 
It often blows back, badly, due to ambiguity.

>   As such, choosing whether or
> not to use these formulations affects the spec.

Agreed, with caution about bending the body closure model around this 
prematurely. If we pull it off, probably the body closures can do the 
same "optimization" -- but it's not clear we can pull it off.

> I agree that it may indeed be too large a feature, given that
> desugarings can cover the vast majority of use-cases well enough. I
> just thought it worth following the logic through.

Gotcha.

It is tempting to try for this, since it would let a C++-knowing hacker 
write a loop like the one you show below, and have it work -- while the 
common var-capture pigeon-hole problem would be solved too for 
let-bindings captured by body-closures.

>> >  Still trying to be sure you intended a unique and dynamic scope for the
>> >  initializer (first part) of for(let;;).
>
> In the first version, depending on the definition of "dynamic", yes.
> In the second version, no, though instead closures inside the loop
> body get a similarly 'dynamic' scope.
>
> Here's a longer, still informal version of the second.
>
> Given a loop of this form:
> for (let i = 0, inc = function(){i++}; i<N; inc()) { ... }
> We want the loop to run to completion while ensuring that each closure
> within the body gets a copy of 'i' as it existed in the loop iteration
> in which the closure was created. One way to do that is to keep track
> of the closures that get created during a loop iteration and, at the
> end of the loop iteration, give those closures a new environment
> ([[Scope]]) which is a copy of the old one, but with a clone of the
> current loop body environment record replacing the original.
[snip]
> Probably the biggest issue with the above spec is that it assumes
> envRec inside an environment is reference-like, which it could easily
> not be in current implementations, so this variant would also
> introduce an extra indirection in at least some circumstances.

This is a big issue, I agree.

> I support the two main destructurings under consideration right now
> more than I support the above, though.

Any preference for 0th over 1st iteration scope for closures in INIT 
binding initializer expressions?

You and Allen remind me that the whole head needs thought:

   for (INIT; TEST; NEXT) BODY

If we do what Andreas suggests, 0th iteration scope for INIT, then TEST 
must use BODY scope for the iteration that follows if TEST evaluates 
truthy. And NEXT must use a new scope for the next iteration too, 
preparing for TEST.

         enterblock <V>
         INIT
         reenterblock  // Andreas's suggested 0th iteration scope for INIT
         goto L2
     L1: BODY
         reenterblock
         NEXT
     L2: TEST
         iftrue L1
         leaveblock

This is not going to give TEST and NEXT the "DWIM" semantics. Sorry, I'm 
sure you get this, I'm just spelling it out to be sure everyone 
(including me) gets it.

Is this a problem? I don't know. INIT may want the DWIM semantics, but 
TEST and NEXT before it for 2nd through Nth iterations really are more 
like parts of the BODY. Why should they form closures that magically 
reference "current iteration scope" when called later?

DWIM always falls to ambiguity. What did you mean? I dunno, just do it! :-P

/be


More information about the es-discuss mailing list