Jumping in late, and possibly repeating some already covered ground, but this 
is clearly an important topic.

Looking at all the proposals on these threads, I have to throw support behind 
Mark's proposal below, or a close variant of it as discussed below and in some 
of the other branches (I think this is close to what Brendan has been 
advocating as well?).  Let me try to motivate this from a somewhat different 
direction.  Wall of text below - but the punchline is:

    Let's avoid versioning by making (almost) no breaking changes.

# Versioning #
There are two goals to versioning - (1) allow breaking changes that code can 
explicitly opt-in to and (2) support reasonable forward compatibility of new 
code running on old runtimes.

The original plan for ES6 was to support (1) and (2) via a versioned script 
tag.  This allowed breaking changes because it was explicit opt in, and 
supported forward compatibility to the extent that there was graceful 
degradation of behaviour on old runtimes (skipping the script block).

Versioned script tags are onerous though, and folks are rightly looking for 
better options.

The better option is to not do versioning at all.  To do that, you need to give 
up on (1) and (2).  That is - no breaking changes (or at least no practically 
significant ones), and no 1st class support for forward compatibility.

# Forward compatibility #
The forward compatibility point in favor of a versioned script tag was never 
particularly strong anyway.  A versioned script tag is ignored on most down-rev 
browsers, but it is still hard to construct programs using new syntax that 
behave pleasantly on those browsers, because the granularity of feature 
detection is at the script-block level, not the application-feature level.  To 
the extent that apps want to use new syntax and still work on older browsers, 
they can almost as easily just accept the early errors from their <script> 
blocks and detect whether they got past early errors successfully in a later 
block in the same way they would with a versioned script tag.  So giving up the 
forward compatibility support doesn't appear to sacrifice much.

# Breaking changes #
Breaking changes are different.  There are quite a lot of breaking changes in 
the current ES6 proposals.  This is somewhat unsurprising, because for the 
history of development of ES6 proposals to date, the assumption has been that 
ES6 will be explicit opt-in for syntax.  So we accepted all sorts of breaking 
changes based on that barrier to entry, and developer expectations associated 
with explicit opt-in.  But now we want to not do versioning for ES6.  So the 
bar for breaking changes should necessarily be much, much higher.

As Mark noted below, this higher bar should effectively disqualify "typeof 
null", completion reform (if the break is significant enough to matter in 
practice), and removal of the global object from the scope chain.  I believe 
these are reasonable things to give up in favor of not having to version.

The next big breaking change is implicit strict mode in ES6.  Because it was 
designed with an explicit opt-in in mind, strict mode has lots of breaking 
changes which we can never offer unversioned.  As with the above, we should 
give this up.  The result is effectively what Mark describes - "use strict" 
remains the opt-in to the strict mode breaking changes.

The last set of breaking changes are reserved words.  As Mark notes, in strict 
mode many of the new keywords are already reserved, and so strict mode can 
allow "let" et al.

However, I think we can go further than that (as Brendan and Allen I think also 
pointed out?).  In Dave's original proposal, he showed how "module" can be 
introduced as a contextual keyword.  I believe we can do this for many more of 
the features as well.  And for those that can't, we should look for potential 
alternative syntax that can be introduced as contextual keywords.

Classes were raised as a counter example here in a branch of the thread.  But 
it seems they can be handled nearly identically to "module" - as a contextual 
keyword with appropriate restricted productions.

let[x]=[5] was also raised as well on one of these threads, as a reason why 
"let" cannot be made contextual.  This one does seem harder to get out of - but 
given how corner case this break is, we could allow this to be resolved as a 
let binding and accept the very minor breaking change in just this corner case, 
treating "let" as a contextual keyword elsewhere.

So the extension to Mark's proposal is that most (possibly all) ES6 syntax is 
also allowed without "use strict", but with the design modified wherever 
necessary to be (almost) fully backward compatible.

# Why 'implicit explicit opt-in' doesn't seem reasonable #

The prevalent alternatives presented in this thread are variations of "implicit 
explicit opt-in", where use of some new syntax causes some part of the code 
inside or outside of it to start behaving differently (breaking changes).  I 
think in practice this will be very confusing.  Take this:

  var x = typeof null;
  module {
    var y = typeof null;
    x == y // false!
  }

This is a refactoring hazard, much harder to find by code inspection than "use 
strict", and just plain confusing.  Alternatives like having a "let" inside a 
function body automatically opt the body into the sort of behaviour above feel 
ever more magic, and very hard to reason about thoroughly.

Moreover, these breaking changes all come at conceptual cost for JavaScript 
developers.  While we may think we are making things better by "fixing" typeof, 
we are actually just making the section on typeof in Doug's slide deck longer - 
he needs to describe both behaviours, and when to expect each.  We already see 
this with strict mode - the answer to "what does this JavaScript code do?" now 
often has to be answered by "well, if it's in strict mode... otherwise...", 
instead of a direct simple (even if not desired) answer.  It is even worse in 
these "implicit explicit opt-in" models.  In those cases, the answer becomes 
"well, if it's inside a 'module', or in strict mode, or inside a function which 
contains anywhere inside it a 'let', or...".

Breaking changes, especially if opted into through "implicit explicit opt-in" 
add to the total complexity of the language.  Moreover, if there aren't 
breaking changes, there is no need for "opt-in" at all.

# Conclusion #
Let's avoid versioning by making (almost) no breaking changes.

Luke

From: es-discuss-boun...@mozilla.org [mailto:es-discuss-boun...@mozilla.org] On 
Behalf Of Mark S. Miller
Sent: Tuesday, January 03, 2012 1:24 PM
To: Allen Wirfs-Brock
Cc: Brendan Eich; es-discuss Steen
Subject: Re: ES6 doesn't need opt-in

                           Just Two Modes


This is a long thread and I've been completely busy with other things so have 
not had time to do more than skim. So please understand if the post below 
misses some context. The following is a summary of some principles that Dave 
and just agreed to in a verbal conversation, but he hasn't had the chance to 
look at the following text before I send it, so it may not quite speak for our 
agreement -- I've substantially elaborated it since the text that Dave and I 
looked at together. Dave introduced this thread with the slogan "just one 
JavaScript", so I'll introduce the following with the (much less catchy) slogan 
"just two modes".



* ES5's strict vs non-strict distinction remains the only mode distinction. ES6 
thus has only the same two modes.

* ES6 non-strict mode must be practically upwards compatible from ES5 
non-strict mode.

* ES6 strict mode must be practically upwards compatible from ES5 strict mode.

* In ES6, one can opt-in to strict mode in at least the following two ways:

    "use strict"; // exactly as in ES5

or

    module <ident>? {

in statement context. In other words, exactly as ES5 may begin strict mode at a 
function boundary to apply to everything recursively contained lexically within 
the function, in ES6 in addition, strict mode may also begin at a module 
boundary and apply to everything recursively within the module. Code 
recursively contained within a module is always strict; there's no way to write 
non-strict code within a module. But a module, like a function, may be embedded 
within a non-strict context.

* Code that contains such a module construct may run on an ES5 system or may 
cause an early SyntaxError, depending on whether this ES5 implementation has 
been extended to recognize the module construct. An ES6 system must of course 
recognize the module construct. Thus, modules, as well as most other features 
of ES6, may be deployed incrementally, just as many features of ES5 were 
deployed incrementally in the transition from ES3 to ES5.

* We give up typeof reform.

* We do completion reform only if we judge it to be practically upward 
compatible, with a dispensation to ES5 implementations to implement it without 
penalty of being non-conformant. (Dave and I both expect it will in fact be 
practically upwards compatible.)

* As with completion reform, if there are other cleanups we can make to ES5 
that is practically upwards compatible, e.g., whose only incompatibility is 
with test262, we can consider these for ES6 and absolve ES5 systems that adopt 
these cleanups.

* We obtain a clean top level scope only by using loaders, which is 
increasingly how I've been thinking of SES anyway.

* The identifiers that are reserved in ES5 only in strict mode are:

     implements, interface, let, package, private, protected, public, static, 
yield
ES6 features that use these keywords are available only in strict mode. Because 
ES5 reserved them, this is fully upwards compatible with ES5. For other ES6 
features that do not depend on these keywords, whether or not they must also be 
available in ES6 non-strict code we need to take on a case by case basis.

* In ES6, nested named function declaration must be accepted and have the 
agreed ES6 semantics in strict code. As advised at 
<http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls>,
 all major browsers currently reject nested named function declaration in 
strict code, so accepting them with the agreed semantics in ES6-strict is fully 
upwards compatible.ES6 remains as silent as ES5 about whether nested named 
function declarations can appear in non-strict code or what their semantics is 
there.

--
    Cheers,
    --MarkM

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

Reply via email to