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