Brendan Eich wrote: > [Grant Husbands wrote:] >> 'Note' all closures (dynamically) created in (lexically) the loop >> initializer. > > Only in the initializer? Why should closures formed there be dynamically > scoped to the current iteration?
Because that directly serves the use cases that have for-initialization-based closures modifying loop variables. None of the simple desugarings do that, beyond perhaps the first iteration. > Instead [this requires] a dynamic scope for closures in the initializer [...] > Dynamic scope is a warning sign, almost always a mistake. Indeed, given the eval shape-changing problem you pointed out, this cannot be properly resolved, in the first form of this proposal. The second form might not require generalised dynamic scope, though; it requires an operation that clones a scope with a single environment record changed, though. More on that, later. >> Or, here's one that copies the other way (and is probably cleaner): >> 'Note' all closures (dynamically) created in (lexically, >> post-desugaring) the loop body. Each time you end an iteration, update >> all the loop variable activation record pointers to point at a new >> clone of that activation record. > > This is a more complex spec than one that models each iteration having its > own lexical scope. The spec needs only declarative environments, not hidden > references and pointer updates. I think there may be a misunderstanding, as the operation I'm talking about can be given in spec language without talk of references and pointer updates. I'll write it out at the end of this email. > As an implementation technique, Chez Scheme's heap boxing and assignment > conversion could be even better. Indeed, and that introduction of a level of indirection for affected variables is what Herby was effectively suggesting, if I'm understanding you both correctly. I was trying to suggest something I thought was more compatible with the current specification. > 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. As such, choosing whether or not to use these formulations affects the spec. 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. > 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. In terms of spec: The 'add the closure to a list' operation could be described using a desugaring. The 'scope clone with one record changed' operation and the end-of-iteration use of it would require spec changes. In spec language, the main operation would probably be like this (I'm cargo-culting the format, though, so I apologise if it has idiosyncrasies): 10.2.2.9 CopyDeclarativeEnvironment (E, C) When the abstract operation CopyDeclarativeEnvironment is called with a Lexical Environment as argument E and a Lexical Environment as argument C the following steps are performed: 1. Let env be a new Lexical Environment. 2. Let lastOuter be the outer lexical environment of E 3. If E and C are the same: a. Let envRec be a copy of the environment record of E. b. Set env's environment to be envRec c. Set the outer lexical environment reference of env to lastOuter 4. Else a. Set the environment record of env to be the environment record of E b. Let outer be the result of calling CopyDeclarativeEnvironment passing lastOuter and C as the arguments c. Set the outer lexical environment reference of env to be outer 5. Return env. 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. I support the two main destructurings under consideration right now more than I support the above, though. Regards, Grant. _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss