Four static scoping violations in ES5 sloppy
On Sun, Mar 17, 2013 at 11:44 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
- implicit global variable creation
with
delete
ing free variableseval
introducing local bindings2 and 4 make perfect sense, but I don't understand how 1 and 3 interfere with static scoping. In particular, given a language with no
with
and with ES5-strict semantics foreval
, I was unable to contrive scenarios where implicit global variable creation ordelete
ing a free variable introduced an ambiguity in the scope chain that prevented static knowledge of what an identifier referred to.Does anyone have any idea how 1 and 3 interfere with static scoping?
The point is that given these features, you can't predict statically whether a variable reference is bound to a global variable, or is unbound and will produce a ReferenceError.
Here's case 1:
if (something_random()) window.xxx = 7; xxx; // ReferenceError or not?
And here's case 3:
if (something_else_random()) delete xxx; xxx; // ReferenceError or not?
On Mar 18, 2013, at 7:54, "Sam Tobin-Hochstadt" <samth at ccs.neu.edu> wrote:
On Sun, Mar 17, 2013 at 11:44 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
- implicit global variable creation
with
delete
ing free variableseval
introducing local bindings2 and 4 make perfect sense, but I don't understand how 1 and 3 interfere with static scoping. In particular, given a language with no
with
and with ES5-strict semantics foreval
, I was unable to contrive scenarios where implicit global variable creation ordelete
ing a free variable introduced an ambiguity in the scope chain that prevented static knowledge of what an identifier referred to.Does anyone have any idea how 1 and 3 interfere with static scoping?
The point is that given these features, you can't predict statically whether a variable reference is bound to a global variable, or is unbound and will produce a ReferenceError.
Here's case 1:
if (something_random()) window.xxx = 7; xxx; // ReferenceError or not?
And here's case 3:
if (something_else_random()) delete xxx; xxx; // ReferenceError or not?
Thanks, but what does your example for case 1 have to do with implicit global variable creation? It actually demonstrates how the situation is exactly the same with explicit global variable creation. Strict mode does nothing to change the result.
Similarly, in case 3, the code could be delete window.xxx
and we still cannot statically predict whether there's a ReferenceError
on the subsequent line or not. Again, strict mode does not change the result.
On 18 March 2013 13:28, Domenic Denicola <domenic at domenicdenicola.com> wrote:
On Mar 18, 2013, at 7:54, "Sam Tobin-Hochstadt" <samth at ccs.neu.edu> wrote:
On Sun, Mar 17, 2013 at 11:44 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
- implicit global variable creation
with
delete
ing free variableseval
introducing local bindings2 and 4 make perfect sense, but I don't understand how 1 and 3 interfere with static scoping. In particular, given a language with no
with
and with ES5-strict semantics foreval
, I was unable to contrive scenarios where implicit global variable creation ordelete
ing a free variable introduced an ambiguity in the scope chain that prevented static knowledge of what an identifier referred to.Does anyone have any idea how 1 and 3 interfere with static scoping?
The point is that given these features, you can't predict statically whether a variable reference is bound to a global variable, or is unbound and will produce a ReferenceError.
Here's case 1:
if (something_random()) window.xxx = 7; xxx; // ReferenceError or not?
And here's case 3:
if (something_else_random()) delete xxx; xxx; // ReferenceError or not?
Thanks, but what does your example for case 1 have to do with implicit global variable creation?
Nothing, but Sam probably meant to write:
if (something_random()) xxx = 7; xxx; // ReferenceError or not?
It actually demonstrates how the situation is exactly the same with explicit global variable creation. Strict mode does nothing to change the result.
Similarly, in case 3, the code could be
delete window.xxx
and we still cannot statically predict whether there's aReferenceError
on the subsequent line or not. Again, strict mode does not change the result.
That is true, and the proper answer is that the global object should be on the above list as well. Not sure why Mark did not include it.
On Mon, Mar 18, 2013 at 8:40 AM, Andreas Rossberg <rossberg at google.com> wrote:
Nothing, but Sam probably meant to write:
if (something_random()) xxx = 7; xxx; // ReferenceError or not?
Right, I was confusing the problem this code describes, which is what I think Mark meant, with the problem of the global object being on the scope chain.
It actually demonstrates how the situation is exactly the same with explicit global variable creation. Strict mode does nothing to change the result.
Similarly, in case 3, the code could be
delete window.xxx
and we still cannot statically predict whether there's aReferenceError
on the subsequent line or not. Again, strict mode does not change the result.That is true, and the proper answer is that the global object should be on the above list as well. Not sure why Mark did not include it.
I believe that Mark would distinguish 1-4, which are fixed by strict mode, from the global object, which is the remaining issue in ES5. But only Mark can answer this for sure.
From: Sam Tobin-Hochstadt
On Mon, Mar 18, 2013 at 8:40 AM, Andreas Rossberg <rossberg at google.com> wrote:
Nothing, but Sam probably meant to write:
if (something_random()) xxx = 7; xxx; // ReferenceError or not?
Right, I was confusing the problem this code describes, which is what I think Mark meant, with the problem of the global object being on the scope chain.
It actually demonstrates how the situation is exactly the same with explicit global variable creation. Strict mode does nothing to change the result.
Similarly, in case 3, the code could be
delete window.xxx
and we still cannot statically predict whether there's aReferenceError
on the subsequent line or not. Again, strict mode does not change the result.That is true, and the proper answer is that the global object should be on the above list as well. Not sure why Mark did not include it.
I believe that Mark would distinguish 1-4, which are fixed by strict mode, from the global object, which is the remaining issue in ES5.
Last time this came up:
www.mail-archive.com/[email protected]/msg18463.html
it was clarified that the global object is not a static scoping violation, since it's the top-most in the scope chain. Thus we know that every binding that's not in the nested function environments must be on the global object—or not exist.
That's the essence of my confusion. Cases 1 and 3 do not change things. That is, xxx = 7
versus window.xxx = 7
doesn't change where we determine xxx
to be: it must be global (if it exists at all). Similarly, delete xxx
versus delete window.xxx
doesn't change that xxx
must be global or not exist. And strict mode prohibits the former of these two forms, but doesn't prohibit the latter, and so seems to have no impact.
Now, if with
and sloppy eval
weren't removed, I could see the argument, at least for delete
:
www.mail-archive.com/[email protected]/msg18466.html
but with those two gone (i.e. with 2 and 4 gone), 1 and 3 seem to not matter.
But only Mark can answer this for sure.
Indeed :)
Interesting thread. After all this work trying to figure out what I meant, I'm sorry to disappoint. Technically, Domenic is right: once we are rid of "with" and non-strict "eval", #1 and #3 do not technically violate the degree of static scoping provided by ES5.
So why are they on this list?
Because many who are not already intimately familiar with JS will be properly outraged at #1 and #3 in non-strict mode, and appreciate that strict mode cleans up scoping to the extent that it can.
And why does ES5/strict impose these restrictions, when they are not necessary for the formal criterion?
Because ES5 strict mode, being an opt-in, gave us a rare opportunity to clean things up in preparation for yet better scoping in ES6. I'm pleased to report that it mostly turned out that way. Because of #1 and #3, ES5 strict code will be easier to refactor into ES6 modules, where the global object is finally not on their scope chain. At the time we did this, we didn't anticipate this specific aspect of ES6, but took the opportunity to clear the ground.
And because implicit creation of global variables was incredibly hazardous, turning misspellings into new global variables.
On 18 March 2013 17:32, Mark S. Miller <erights at google.com> wrote:
And why does ES5/strict impose these restrictions, when they are not necessary for the formal criterion?
Because ES5 strict mode, being an opt-in, gave us a rare opportunity to clean things up in preparation for yet better scoping in ES6. I'm pleased to report that it mostly turned out that way. Because of #1 and #3, ES5 strict code will be easier to refactor into ES6 modules, where the global object is finally not on their scope chain. At the time we did this, we didn't anticipate this specific aspect of ES6, but took the opportunity to clear the ground.
Maybe I misunderstand what you mean, but unfortunately, the global object will remain at the top of the scope chain in ES6, even with modules (though complemented with a lexical environment for new binding forms). We shied away from fixing that mistake.
Andreas Rossberg wrote:
On 18 March 2013 17:32, Mark S. Miller<erights at google.com> wrote:
And why does ES5/strict impose these restrictions, when they are not necessary for the formal criterion?
Because ES5 strict mode, being an opt-in, gave us a rare opportunity to clean things up in preparation for yet better scoping in ES6. I'm pleased to report that it mostly turned out that way. Because of #1 and #3, ES5 strict code will be easier to refactor into ES6 modules, where the global object is finally not on their scope chain. At the time we did this, we didn't anticipate this specific aspect of ES6, but took the opportunity to clear the ground.
Maybe I misunderstand what you mean, but unfortunately, the global object will remain at the top of the scope chain in ES6, even with modules (though complemented with a lexical environment for new binding forms). We shied away from fixing that mistake.
Don't break the web.
Versioning is an anti-pattern.
I don't think "shied away" is accurate. We couldn't fix that mistake.
On 18 March 2013 17:48, Brendan Eich <brendan at mozilla.com> wrote:
Andreas Rossberg wrote:
On 18 March 2013 17:32, Mark S. Miller<erights at google.com> wrote:
And why does ES5/strict impose these restrictions, when they are not necessary for the formal criterion?
Because ES5 strict mode, being an opt-in, gave us a rare opportunity to clean things up in preparation for yet better scoping in ES6. I'm pleased to report that it mostly turned out that way. Because of #1 and #3, ES5 strict code will be easier to refactor into ES6 modules, where the global object is finally not on their scope chain. At the time we did this, we didn't anticipate this specific aspect of ES6, but took the opportunity to clear the ground.
Maybe I misunderstand what you mean, but unfortunately, the global object will remain at the top of the scope chain in ES6, even with modules (though complemented with a lexical environment for new binding forms). We shied away from fixing that mistake.
Don't break the web.
Versioning is an anti-pattern.
I don't think "shied away" is accurate. We couldn't fix that mistake.
I think we could have fixed it for modules. Whether that would have been worth it I don't know.
Le 18/03/2013 17:48, Brendan Eich a écrit :
Andreas Rossberg wrote:
On 18 March 2013 17:32, Mark S. Miller<erights at google.com> wrote:
And why does ES5/strict impose these restrictions, when they are not necessary for the formal criterion?
Because ES5 strict mode, being an opt-in, gave us a rare opportunity to clean things up in preparation for yet better scoping in ES6. I'm pleased to report that it mostly turned out that way. Because of #1 and #3, ES5 strict code will be easier to refactor into ES6 modules, where the global object is finally not on their scope chain. At the time we did this, we didn't anticipate this specific aspect of ES6, but took the opportunity to clear the ground.
Maybe I misunderstand what you mean, but unfortunately, the global object will remain at the top of the scope chain in ES6, even with modules (though complemented with a lexical environment for new binding forms). We shied away from fixing that mistake.
Don't break the web.
Versioning is an anti-pattern.
I don't think "shied away" is accurate. We couldn't fix that mistake.
I don't understand the mention of "don't break the web". Modules aren't used on the web, so whatever rules are chosen for them they can't break the web. I'm probably missing something here.
Maybe "global in the scope chain" has to be divided into 2 different meanings: "read" and "write" to the global scope. My current understanding is as follow:
- code in a module body can read globals in the global scope
- code in a module cannot create a global properties (unless being hand off the global object specifically)
- the module name is part of the newly introduced lexical binding ("file local" global variables).
How far am I in understanding modules and scoping?
Sadness. I had misremembered how all that finally resolved. In that moment, I thought we had decided to fix that for modules. But now that you mention it, I remember the discussion. These are difficult issues, and I was not trying to suggest we reopen them.
In any case, regarding the new #1 and #3 restrictions in ES5 strict, the most we can say is that it cleared the ground in hopes of better scoping in ES6, but it didn't turn out that way. Oh well. Was worth a shot.
Wasn't there some talk about an option to remove global from a module's scope chain? Probably in the module loader? I seem to remember that being discussed. I don't, however, remember ever seeing a decision for or against such a solution.
Andreas Rossberg wrote:
On 18 March 2013 17:48, Brendan Eich<brendan at mozilla.com> wrote:
Andreas Rossberg wrote:
Maybe I misunderstand what you mean, but unfortunately, the global object will remain at the top of the scope chain in ES6, even with modules (though complemented with a lexical environment for new binding forms). We shied away from fixing that mistake. Don't break the web.
Versioning is an anti-pattern.
I don't think "shied away" is accurate. We couldn't fix that mistake.
I think we could have fixed it for modules. Whether that would have been worth it I don't know.
For modules with the standard loader, it would be not web-breaking but pretty code-migration-hostile to remove the global.
With a custom loader, sure. Possibly this can be made into a one-liner.
On Mon, Mar 18, 2013 at 1:17 PM, Brendan Eich <brendan at mozilla.com> wrote:
With a custom loader, sure. Possibly this can be made into a one-liner.
The constructor for new loaders takes a global as an argument. So yes, this is a one-liner.
I went back to this old es-discuss thread:
www.mail-archive.com/[email protected]/msg18408.html
which references a video segment from Mark Miller:
www.youtube.com/watch?v=Kq4FpMe6cRs&t=42m53s
giving four static scoping violations:
with
delete
ing free variableseval
introducing local bindings2 and 4 make perfect sense, but I don't understand how 1 and 3 interfere with static scoping. In particular, given a language with no
with
and with ES5-strict semantics foreval
, I was unable to contrive scenarios where implicit global variable creation ordelete
ing a free variable introduced an ambiguity in the scope chain that prevented static knowledge of what an identifier referred to.Does anyone have any idea how 1 and 3 interfere with static scoping?