On Feb 3, 2012, at 12:44 PM, Jason Orendorff wrote:

> On Fri, Feb 3, 2012 at 11:23 AM, Allen Wirfs-Brock
> <al...@wirfs-brock.com> wrote:
>> On Feb 3, 2012, at 6:42 AM, Jason Orendorff wrote:
>>>    for (let V in EXPR) STMT
>>>    for (let V of EXPR) STMT
>>>    for (let V = EXPR; ...; ...) STMT
>> 
>> the third case is different in several ways. Including:
>>   let V =EXPR is a distinct syntactic pattern that has a specific semantics  
>> that requires that the EXPR is evaluated in the same scope as EXPR. However, 
>> in most cases (but consider |et x=(x=5,++x);) all of EXPR will be in V's 
>> temporal dead zone.
>>   The actual syntax is for (let V1=EXPR1, V2=EXPR2,...;...) STMT Again, the 
>> semantics for such declarations is that all of the Vs are defined in the 
>> same scope and EXPRn+1 is outside the TDZ for for Vn.
>> 
>> Using your semantics for the 3rd form would mean that its let clause was not 
>> consistent with let declarations in all other contexts.
> 
> The behavior of let-declarations is that the name hoists to the
> enclosing block. We don't want to be *that* consistent.
> 
> So the situation ends up being: we will have a statement that looks
> like "for(let V = ...)". It can either be partly consistent with other
> statements that look like "for (let ...)" or partly consistent with
> other statements that look like "let V = ...".

I think that the best way to think about this is that all for statements with 
let/const declaration implicit introduce a block to contain those declarations. 
 The base question here is the extent of that block. Does it surround the 
entire for statement,  or just the statement part of the for statement.  Or 
does it need two blocks, one surrounding the for and one surrounding the 
statement. 

The |let| is "inside" the |for| so I think |for| bounded hoisting is fairly 
consistent with general let declarations.

> 
> Either choice breaks with consistency on one side; TC39 should pick
> the way that astonishes the fewest people.
> 
>>> What is even more important than consistency is that the language work
>>> for people. We know that *not* having per-iteration bindings
>>> astonishes users, because that's how SpiderMonkey does it, and people
>>> are regularly astonished.
>> 
>> I think you are over generalizing from a specific use case.  This bug cuts 
>> both ways.


Just to reinforce something I think I already said.  I'm not questioning the 
desirability of  per iteration bindings for for-in/for-of.  I'm saying that in 
some cases (not most) that people who use the fully generality of the for(;;) 
statement will be astonished.  I think this is even recognized by you in your 
desugaring by the fact that you use %tmp to propagate the value of V from one 
iteration to the next.  It would be astonishing that a V++ in the loop body did 
not change the value of V seen by the next iteration.

That desugarings work fine in that regard as long as there is no closure 
capture involved. But consider:
 
   for (let keepGoing=true, stopper=function() {keepGoing=false}; keepGoing;) {
      let thisOne = getNext();
      if (thisOne == null) keepGoing = false;
      else thisOne.scanForSomeSpecificConditionAndIfDetectedDo(stopper);
   }

There are lots of other ways to structure such a loop. But given the observable 
value propagation from iteration to iteration, it isn't unreasonable for a 
programmer to expect this to work.  They will be surprised that it doesn't.

> 
> Could you explain a little more why you think that's the case?
> 
> Grepping finds 1,227 loops of the "for (let ...; ...; ...)" variety in
> Mozilla's codebase. Of these 95% are simple counting loops, and *none*
> contain functions in the loop-head. I estimate about 4% of these 1,227
> loops have functions in the body, most of which escape, but it is
> harder to put precise numbers on that. Loops where the loop variables
> are modified in the loop body seem rare (I didn't see any), but I
> can't say how rare.

It might also be useful to look at for(var ...;...;...) and for (...;...;...) 
loops.  Nobody is proposing changing the scoping for those, but they might 
provide a broader view of how people use the generality of for(;;) loops

> 
> Of the non-simple-counting loops, here is a sample:
> 
>    for (let child = element.firstChild; child; child = child.nextSibling) ...
>    for (let frame = aElement.ownerDocument.defaultView; frame !=
> content; frame = frame.parent) ...
>    for (let row; (row = aResultSet.getNextRow());) ...
the above is a little odd
>    for (let i = 0; this[i] != null; i++) ...
> 
> In all these cases, per-iteration bindings would do the right thing.
> 
> If the choice comes down to astonishing this programmer:
> 
>   for (let i = 0; i < n; i++) {
>       buttons[i] = makeButton();
>       buttons[i].click(function () { alert("pushed button " + i); });
>   }

I would actually feel a lot more comfortable with per iteration binding of 
for(;;) if the for let declaration was limited to a single binding.  In that 
case, a programmer really has to go out of their way to closure capture the 
outer for bindings. I'd have no problem with saying that my above example would 
have to be written as:

{ let let keepGoing=true; stopper=function() {keepGoing=false};   
   for (; keepGoing;) {   //aka while
      let thisOne = getNext();
      if (thisOne == null) keepGoing = false;
      else thisOne.scanForSomeSpecificConditionAndIfDetectedDo(stopper);
   }
}

> 
> or astonishing this one:
> 
>    let geti;
>    for (let i=(geti = function() {return i},expr), expr, incr =
> function(i++), decr=function(i--), cmp=function(){return i<n};
> cmp();incr()) {
>        let j=i;
>        i +=10;
>        decr();
>        ...
>        If (j+-9 !== i) ...
>        ...
>    }
> 
> then from where I'm sitting it looks like an easy choice. Let's
> support realistic use cases.
> 
> Of course if per-iteration bindings would be confusing in practice,
> that would be bad. But the for-loop head issue should not be a major
> consideration, because people truly almost never write code like that.


But I also have to validly (and hopefully reasonably) specify exactly what 
happens for the unrealistic use cases.  There is a problem with your desugaring 
in that the evaluation of INIT isn't scoped correctly relative to V. Mark's, 
doesn't have this problem, but I'd still have to specify something at least 
semi-sane for any closures in INIT.

> 
> I'd be glad to have range(); however I don't think it's effective to
> address usability issues by offering more alternatives. :-\
> 
> -j
> 

_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to