Brendan Eich wrote: > 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.
I think what you're saying here is that we would only ever need this environment-copying trickery if there were closures in both the init and in the rest, in which case I agree. Let-loops with closures only in the init could then use the simplest semantics and let-loops with closures only in the rest could use any of the desugarings. It's starting to sound like a significant addition to the static semantics, but I'll leave it to others. Otherwise, maybe "Herby's init-swap", below, is what you're after? It only alter closures in the init. > 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 think I'm not understanding something, here, as I'm still not sure how the suggested semantics constitute even a quoted "optimization" (maybe I'm not sure what it's an optimization of), or why we'd need to bend both body closures and init closures in the same runtime, but I'll carry on. > Any preference for 0th over 1st iteration scope for closures in INIT binding > initializer expressions? If the init binding's closures are not going to touch every one of the iterations, I believe they should touch none of them, for consistency (I think that means "0th"). But I don't feel strongly about it, and it does look like implementation detail might favour "1st". [snipped implementation details] > 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. I indeed agree; TEST and NEXT are essentially part of the body, since they have always needed to reference the body's iteration variables and are repeated alongside the loop body. And, to spell things out further, it's the mechanism of "reenterblock" for these loops that this thread is discussing, as I understand it. > Why should they form closures that magically reference "current > iteration scope" when called later? Indeed, closures in TEST and NEXT must clearly be instantiated per iteration and they lie naturally in the body, under all intuitive desugarings, so they might as well be treated in the same manner as the body. > DWIM always falls to ambiguity. What did you mean? I dunno, just do it! :-P Indeed, and individual and sometimes obtuse use cases can have more influence than they perhaps should. (That's not a sly jab at anyone.) Based on my reading of what you wrote, but without being able to point at any one particular quote, I think there may still be a misunderstanding of some kind, so I think we might benefit from me spelling out a run-through under the versions of Herby's idea covered so far. Note that I can't guarantee that this is a correct formulation of Herby's idea, but I am fairly sure it is. Also note that each formulation is entirely distinct. We'd never use both in the same runtime. In each case, the example code is as follows: for (let i=0, inc=function(){++i};i<2;inc()) setTimeout(function(){alert(i)},200); Herby's formulation (and my second variant), which I'll call "Herby's body-swap", would proceed as follows: 1. Scope with i and inc created for the loop. 2. Loop starts. 2a. The inc() isn't run, since this is the first time round the loop. 3. i<2, so we carry on. 4. The body closure gets created and points at the active i, which is 0. 5. The iteration ends and the next one starts. 5a. The body closure that was created gets cloned variables, with i==0. 5b. inc(), so i becomes 1. 6. i<2, so we carry on. 7. The body closure gets created and points at the active i, which is 1. 8. The iteration ends and the next one starts. 8a. The body closure that was created gets cloned variables, with i==1. 8b. inc(), so i becomes 2. 9. i<2 is false, so we're done. At this point, we have two internal closures, with i==0 and i==1, and the inc closure can see i==2, which is indeed the value i would have after the loop was over, which seems intuitive. The mechanism for cloning loop variables could be like any of Herby's suggestions or the CopyDeclarativeEnvironment operation I suggested. Note that that variant only needs one scope for the whole loop, though the cloning operation creates its own. My other formulation, which I'll call "Herby's init-swap", would proceed as follows: 1. Scope with i and inc created for the loop. 2. Loop starts 2a. The loop's implicit internal let block starts and copies i and inc. 2b. The inc gets updated to point at the current iteration scope. 2c. The ++i isn't run, since this is the first time round the loop. 3. i<2, so we carry on. 4. The closure gets created and points at the inner i, which is 0. 5. The iteration ends and the next one starts. 5a. The loop's implicit internal let block copies values from the end of the last 5b. The inc gets updated to point at the current iteration scope. 5c. inc(), so i becomes 1. 6. i<2, so we carry on. 7. The closure gets created and points at the inner i, which is 1. 8. The iteration ends and the next one starts. 8a. The loop's implicit internal let block copies values from the end of the last 8b. The inc gets updated to point at the current iteration scope. 8c. inc(), so i becomes 2. 9. i<2 is false, so we're done. At this point, we have two internal closures, with i==0 and i==1, and the inc closure can see i==2. So the visible behaviour is the same either way. Now, there are some advantages and disadvantages: * Herby's body-swap gives an apparent desugaring with just one scope for the loop variables * Herby's init-swap might need to be concerned about shape changes (due to eval or such). * Herby's init-swap has a consistent set of closures to alter on each iteration (barring nested closures in the init). * Herby's init-swap's variables/records that need replacing are incredibly likely to be topmost in the closure's scope and could be specced as such (which would make it favourable to current runtimes, afaik). * Both of them pretty much require some form of scope cloning or scope modification that does not match anything currently in the spec. * Both of them make closures in the init and in the rest see a consistent world, for as long as is possible, while still giving body closures unique copies per iteration. Sorry for the long email, but I thought the detail could help iron out any of the remaining (lesser) misunderstandings. Regards, Grant. _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss