ES6 “modes” and user-friendliness
On Jan 16, 2012, at 10:30 PM, Axel Rauschmayer wrote:
Lastly, there is one aspect of automatic language version detection that I still don’t understand: With David Herman’s solution, I’d expect browsers to first scan all of the code and then determine what semantics to use (at least conceptually/abstractly, possibly not in the actual implementation):
- Found ES6 language feature => ES6 semantics (a few breaking changes, if any)
- Found "use strict" => ES5.strict semantics
- None of the above => ES3 semantics
I think you've misunderstood. There's nothing in my New Year's email about "getting ES6 semantics" when you find an occurrence of a particular feature (that kind of thing was just a side conversation in the mega-thread, and a very ill-conceived one IMO). The proposal is that within the context of a module, you get a few small changes to the semantics for the code within the module -- and nothing else. There's no scanning for particular features. There's no language version detection. There's no versioning at all. ES6 is an update to the language like every other edition of ECMA-262 has been. Browsers don't have ES3 modes and ES6 modes. They just have ECMAScript.
I think you've misunderstood. There's nothing in my New Year's email about "getting ES6 semantics" when you find an occurrence of a particular feature (that kind of thing was just a side conversation in the mega-thread, and a very ill-conceived one IMO). The proposal is that within the context of a module, you get a few small changes to the semantics for the code within the module -- and nothing else. There's no scanning for particular features. There's no language version detection. There's no versioning at all. ES6 is an update to the language like every other edition of ECMA-262 has been. Browsers don't have ES3 modes and ES6 modes. They just have ECMAScript.
Ah, that makes sense, the thread you mentioned got me confused. Then for language implementors, there are three modes/semantics:
-
module => ES6 – some changes break with ES5.strict
-
"use strict" => ES5.strict + all ES6 constructs that are backward-compatible
-
otherwise => ES3 + all ES6 constructs that are backward-compatible
It’s a tiny bit messy, but I can see that for developers, the illusion of a single ES6 is more or less intact. Seems like the best possible solution.
Given that most people are bound to use modules and given that they are a very convenient “switch”, wouldn’t it be best to introduce as many breaking changes via #1 now (as opposed to later, in ES7 etc.)? Especially removing window from the scope chain.
Mark’s email [1] seems to suggest just two modes (#1 being a superset of #2 = subsuming it), but using module as a switch, distinguishing #1 and #2 might be worth it.
Axel Rauschmayer <mailto:axel at rauschma.de> January 17, 2012 12:06 AM
Ah, that makes sense, the thread you mentioned got me confused. Then for language implementors, there are three modes/semantics:
- module => ES6 – some changes break with ES5.strict
"some changes break with ES5.strict" is confusing -- do you mean 'let' works instead of being a future reserved word, whereas in (3, below) 'let' is not reserved at all?
- "use strict" => ES5.strict + all ES6 constructs that are backward-compatible
- otherwise => ES3 + all ES6 constructs that are backward-compatible
It’s a tiny bit messy, but I can see that for developers, the illusion of a single ES6 is more or less intact. Seems like the best possible solution.
Agreed.
Given that most people are bound to use modules and given that they are a very convenient “switch”, wouldn’t it be best to introduce as many breaking changes via #1 now (as opposed to later, in ES7 etc.)? Especially removing window from the scope chain.
Dave proposed not removing the global object, but keeping the free variable error analysis. Most of the win is in the latter, for users. Indeed removing the global object is harder for implementors who've already sunk the cost, and lots of JS that might want to migrate into module {...} to get early typo errors depends on window.foo aliasing var foo (object detection at top level).
If you mean other breaking runtime changes, please note that early on a bunch of us threw in the towel on typeof null == "null". We're still hopeful for completion reform, but Dave's proposal ups the ante: runtime semantic shifts are bad for users and implementors, worse than before with whole-script opt-in.
Mark’s email [1] seems to suggest just two modes (#1 being a superset of #2 = subsuming it), but using module as a switch, distinguishing #1 and #2 might be worth it.
I think counting modes has to count to three, as you enumerated above (if I understand your (1) correctly).
On Jan 17, 2012, at 12:06 AM, Axel Rauschmayer wrote:
Ah, that makes sense, the thread you mentioned got me confused.
Understandable -- it was a seriously huge thread.
Then for language implementors, there are three modes/semantics:
- module => ES6 – some changes break with ES5.strict
- "use strict" => ES5.strict + all ES6 constructs that are backward-compatible
- otherwise => ES3 + all ES6 constructs that are backward-compatible
Right.
It’s a tiny bit messy, but I can see that for developers, the illusion of a single ES6 is more or less intact. Seems like the best possible solution.
Exactly.
Given that most people are bound to use modules and given that they are a very convenient “switch”, wouldn’t it be best to introduce as many breaking changes via #1 now (as opposed to later, in ES7 etc.)? Especially removing window from the scope chain.
Mostly yes; in particular, modules get their variable references statically checked. But I no longer believe removing window from the scope chain is doable without blowing the "too many modes" budget.
That said, loaders make it possible to create fresh globals, and I still think we should try to introduce a single <meta> tag that makes it easy to opt the rest of the page in to a user-specified loader. But I'm not holding out hope for this to Save the World. :)
Mark’s email [1] seems to suggest just two modes (#1 being a superset of #2 = subsuming it), but using module as a switch, distinguishing #1 and #2 might be worth it.
It doesn't work. You can't get static checking with strict mode alone, because of situations like
with (obj) { (function() { "use strict"; ... })() }
But in practice, strict mode can fade away as a transitional concept from ES5 that, while still spec'ed and implemented, doesn't get used much in practice. Modules carry the torch of ES5-strict and take it even further, and become the actual "mode" that gets used in practice, both because it is a useful feature independent of the language cleanups in carries with it, and because it doesn't require a noisy opt-in pragma.
So the language has 3 modes in the spec, but in practice only 2 that matter.
- module => ES6 – some changes break with ES5.strict
"some changes break with ES5.strict" is confusing -- do you mean 'let' works instead of being a future reserved word, whereas in (3, below) 'let' is not reserved at all?
I’m not sure about specifics, I mean “ES6 things that are not backward-compatible with with ES5.strict” (if those exist, but it seems like they do).
But in practice, strict mode can fade away as a transitional concept from ES5 that, while still spec'ed and implemented, doesn't get used much in practice. Modules carry the torch of ES5-strict and take it even further, and become the actual "mode" that gets used in practice, both because it is a useful feature independent of the language cleanups in carries with it, and because it doesn't require a noisy opt-in pragma.
So the language has 3 modes in the spec, but in practice only 2 that matter.
Crystal clear now,
On Tue, Jan 17, 2012 at 7:30 AM, Axel Rauschmayer <axel at rauschma.de> wrote:
Nitpick
- None of the above => ES3 semantics
That should be ES5 (non-strict) semantics. There are (a few) incompatible changes between ES3 and ES5, and you don't want to reintroduce the ES3 behavior (e.g., RegExp literal instance sharing).
But in practice, strict mode can fade away as a transitional concept from ES5 that, while still spec'ed and implemented, doesn't get used much in practice. Modules carry the torch of ES5-strict and take it even further, and become the actual "mode" that gets used in practice, both because it is a useful feature independent of the language cleanups in carries with it, and because it doesn't require a noisy opt-in pragma.
So the language has 3 modes in the spec, but in practice only 2 that matter.
One more question: Is there an opt-in strategy should a version after ES.next introduce breaking changes? That might be something worth planning for. Presumably modules will make it easier to encapsulate this kind of change.
I think "mode" terminology is quite misleading, particularly in regard to run time semantics. For Es5 (and I believ es6) there is no need of for a global runtime mode bit that is analagous to a cpu mode bit that ambiently changes the semantics of some or all instruction. Instead, we have multiple semantic for some constructs where the appropiate semantics to use can be apriori determined based up lexical context prior to runtime.
From a es programmer's perspectve it is reasonable to think about code as being in a strict context or perhaps an es6 context (a module Context?). But calling such contexts "modes" may create confusion, sometimes even for us.
Most of the extended code early errors I list in a previous message on this thread are examples of changes that are not backward compatable with Es5 strict code that Axel is talking about belowAxel Rauschmayer <axel at rauschma.de> wrote:Ss1. module => ES6 – some changes break with ES5.strict
"some changes break with ES5.strict" is confusing -- do you mean 'let' works instead of being a future reserved word, whereas in (3, below) 'let' is not reserved at all?
I’m not sure about specifics, I mean “ES6 things that are not backward-compatible with with ES5.strict” (if those exist, but it seems like they do).
-- Dr. Axel Rauschmayer axel at rauschma.de, twitter.com/rauschma
Home: rauschma.de Blog: 2ality.com
| OB | NB | ----+----+----+ OC | | | ----+----+----+ NC | | | ----+----+----+
(OC = Old Code, NC = New Code, OB = Old Browser, NB = New Browser)
If we are talking about keeping JavaScript user-friendly, we have actually only tackled the second column: How new browsers handle old code and new code. Old browsers encountering old code is not a problem. But how about new code being encountered by old browsers? I keep thinking that that use case is relevant for the current “mode” discussion: If we achieve perfect user friendliness for new browsers, it is all for naught as soon as you deploy and have to support old browsers.
Lastly, there is one aspect of automatic language version detection that I still don’t understand: With David Herman’s solution, I’d expect browsers to first scan all of the code and then determine what semantics to use (at least conceptually/abstractly, possibly not in the actual implementation):
I’ve seen the idea mentioned that there should be mixed semantics: E.g. ES3 semantics with some ES6 features. When would that matter (except while developing ES6)?