On Feb 4, 2012, at 6:35 PM, Brendan Eich wrote:

> Allen Wirfs-Brock wrote:
>> On Feb 4, 2012, at 12:55 PM, Brendan Eich wrote:
>>> ...
> 
>>   However, I doubt that someone who actually codes a function in a 
>> for(let;;) initializer is going to be thinking, "of course, this only 
>> captures the first iteration bindings".
> 
> Why? The initializer runs first. There are two working possibilities: 
> bindings of the same name in an implicit block around the loop (the "0th 
> iteration scope"), immediately shadowed on first (if the condition is true) 
> by fresh bindings of the same names with values forwarded; or what I propose, 
> the first iteration's bindings.
> 
> Why is the 0th iteration scope better than the 1st iteration scope? It may be 
> that some people think of the INIT part in for (INIT; TEST; UPD) STMT as 
> being "before the loop starts", but others may see it as part of the first 
> iteration.
> 
> If we really do have evidence for the 0th iteration scope (hard to imagine), 
> then that is doable but it costs a bit more in the closure-in-initializer 
> case. All else equal I go with lower cost in that rare case.

In general, the concerns we have to consider here relate to when programmers 
think incorrectly about the for(;;) semantics, not when they are thinking 
correctly.

Why, isn't the programmer thinking about per iteration bindings? Because if 
somebody is actually doing something like the |advance={|| i++}| example, there 
is a good chance that the loop body doesn't contain any closure captures of the 
loop variables and they are likely to be thinking about it as if it had classic 
C for(;;) semantics.

In the case where there are no closure captures in the body we probably all 
expect implementations to actually share a single set of bindings across all 
iterations. If an implementation did that for this case it would yield the 
result the programmer to expecting.  But if an implementation actually did that 
in this case it would be an invalid optimization because of the proposed bind 
to only first iteration semantics.

I think it may be useful to enumerate the cases of interest, of a for(let;;) 
loop that we abstract to: for (LET-BINDINGS=INITRS;TEST;NEXT) BODY

1) No closure captures of LET-BINDINGS in any part of the for
          Probable programmer intent: per loop binding of  LET-BINDINGS, but 
that is indistinguishable from the expected optimization of per iteration 
binding 
2) BODY contains closure captures of LET-BINDINGS; no closure capture of 
LET-BINDINGS in INITRS;TEST;NEXT.
          Probable programmer intent: per iteration bindings captured
3)  No closure captures of LET-BINDINGS in BODY; closure capture of 
LET-BINDINGS in one or more of  INITRS;TEST;NEXT.
          Probable programmer intent: per loop binding of  LET-BINDINGS, but 
this is not a valid optimization of per iteration binding using either the 0th 
iteration or 1st iteration scoping semantics
4)  BODY contains closure captures of LET-BINDINGS; at least one of  
INITRS;TEST;NEXT   contains closure captures of LET-BINDINGS.
          Probable programmer intent: ambiguous. Only consistent interpretation 
is per loop binding of  LET-BINDINGS, but high probably for a per iteration 
expectation plus buggy thinking.

For me, a couple things stand out from this analysis
*    Testing for capture of specific bindings seems like a pretty 
straightforward static  semantics.  It seems quite doable to make case 4 an 
early error. 
*    It is closure captures in BODY that wants per iteration instead of per 
loop.  Otherwise programmer intent is probably per loop.
*    Cases 1 to 3 would all DWIM If we had a semantics that could be validly 
optimized to per loop bindings for case 3.  But I haven't found such a 
semantics.

> 
>> With the TDZ alternative I proposed, there  would still be equivalence for:
>>      for(let x=x;;)...;
>> and
>>      for(let x={|| x}();;)...;
>> 
>> Both throw for accessing an initialized variable.
> 
> Yippee. :-|
> 
> Making a dynamic error trap for no good reason is a mistake. Where's the 
> evidence that closures in initializers that capture loop control variables 
> are "wrong"? There isn't any, AFAIK.

The dynamic trap is simply the TDZ semantics.  Whether capture initializer 
capture of loop var is wrong or not completely depends upon the intended use in 
the context of the actual semantics.  I haven't yet thought of any plausible 
use cases where where the intent is first iteration capture.  

But, as noted above a early error for such captures may be a viable alternative.

Allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to