excluding features from sloppy mode

Brendan Eich brendan at mozilla.com
Mon Dec 31 12:06:56 PST 2012


Kevin Smith wrote:
>
>         (b) ES6 sloppy mode cannot have "let".
>
>
>     That's overstated, and as such it contradicts the tenuous
>     consensus of the last TC39 meeting: to reserve 'let' contextually
>     in sloppy mode and evangelize any let [x] = y code out there
>     already (which we've yet to find -- 'let' is used as an
>     identifier, short for 'letter', but not indexed into on LHS).
>
>     The rest of your reasoning is therefore overstated. It does not
>     follow if we can reserve 'let' in ES6, mode-free.
>
>
> Yes, but see below...

Ok!

>
>     Here I want to refine my Platonic prettiness critique. You talk
>     about ES6 as if it can be a thing, which has a timeless existence
>     and truth and beauty. Kind of like a dodecahedron -- a pure "Math
>     thing".
>
>
> It most certainly is ; )

Good so far.

>     What I think "exists": ES6 as a practical language with a large
>     user community who apprehend it in pieces, and sometimes using
>     different metaphors or incomplete and even inconsistent "theories"
>     or schools of thought. It's a human thing, not a Math thing.
>
>
> It should *strive* toward truth and beauty.  It should approach 
> Math-y-ness.  The Math-y-ness is what allows us to cut through all of 
> that human social BS.  Anyway, enough of philosophy;  more practical 
> stuff below...

The philosophy matters. It's an old debate, Plato vs. Aristotle 
(remember Crock and me at TXJS 2011?). I'm a realist, and the problems 
I'm citing are not just "human social BS" -- they involve human factors, 
usability, stuff that is not social but individual and primal (like 
forgetting "use strict"; or [for me] forgetting to turn off the 
Christmas lights each night :-P).

>
>     We agreed at the last meeting to treat 'let' as a contextual
>     keyword at start of statement if followed by an Identifier, '{' or
>     '['. That is a special case, but so what? 'module' was not
>     reserved in ES1-5 so it needs a special case too.
>
>
> Sure, but as I argued when I read about that consensus, it creates a 
> situation where "let" is a second-class identifier.  Either it's an 
> identifier, or it isn't.  Any middle ground is, well, sloppy!  Not 
> Math-y.  : )

Here again we already do not have the ideal as a realistic option.

Consider that 'get' and 'set' are contextual in ES5, but without any 
backward compatibility issues, and based on syntax extensions from 
SpiderMonkey that other engines reverse engineered. My point is that ES3 
could not future-proof by reserving such short words, and did not need 
to. No one can foresee all future final/winning best-syntax designs.

And again, ES5 failed to reserve 'module' in strict mode, and ES1-3 
never reserved 'module', so ES6 must make 'module' only contextually 
reserved. We are already in "either it's an identifier, or it isn't" 
default. If we can do it for 'module', why not for 'let'?

Holding the line at 1 is easier than at 2, but is the line worth 
holding? For what ideal, given the 'module' exception?

>     The spec must serve the users. Not the other way around. Or would
>     you give up 'module' from sloppy mode?
>
>
> Personally, yes.  Only modules loaded by the module loader are 
> implicitly strict.

Now I'm confused. What about module M {...} (inline body)? What does 
"the module loader" mean, the system loader?

Simplicity and elegance are already gone if we're unsure of the 
conditions for the first exception to the rule. There will be more 
exceptions over time, based on our concrete experience over the last 16 
years of ECMA-262 -- I'm pretty sure!

>
>     Imagine you are adapting existing code, including popular
>     libraries not yet in strict mode, to a new app targeting ES6 or
>     better runtimes directly, and ES5 and older via a compiler such as
>     https://github.com/matthewrobb/six. You are not yet be ready to
>     "use strict" in the concatenation of all the code -- there's too
>     much risk of breaking something, and never enough test coverage.
>
>     You want to use ES6 features such as rest parameters and
>     destructuring right away, before any "use strict" or module
>     wrapping. Under your proposal, you can't. You have to multiply
>     risks and spend time turning on strict mode, testing harder in old
>     and new and middling (IE9, anyone?) browsers.
>
>
> Sorry, I don't buy it.
>
> If you have a legacy codebase, and you want to use some ES6 features 
> without upgrading the whole thing to strict mode, then you can 
> selectively upgrade the region that you are working on to strict mode 
> by putting "use strict" inside of a function.

And checking whether the function uses |this| and some no caller uses a 
global reference as the callee expression, intentionally passing |this| 
bound to the global object. There are other potential gotchas.

>  If that's too loose-grained, then you shouldn't be fiddling with new 
> features at all.

Says you! :-)

Functions are not all small and simple in real code. Sure, a quick check 
for arguments, eval, and |this| usage should be enough -- but now I'm 
not using the new ES6 syntax to solve the problem at hand, I'm off doing 
strict-mode conversion for Kevin.

What's more, this will litter functions under maintenace and 
ES6-incremental upgrade with "use strict", and then one will have to go 
back later and do more sweeping stoop-labor, picking up the redundant 
"use strict"; clutter and commoning it at a higher level via a module or 
a single "use strict" in an outer function or global code.

No one works for free. There has to be a motivation. You wrote of 
"carrots" and the ES6 features are indeed sweet. Your "use strict" 
fetish is still a stick, not a carrot. You're trying to get the horse 
(developer) to take a whack by feeding it carrots after. Sadistic! :-/

> And if you're in a situation where you can't go strict, then you 
> surely aren't ready to depend on transcompilation.

That's obviously not true. CoffeeScript does not generate strict code yet!

>     In 10 years, you want everyone to use modules, but there'll still
>     be top-level code, rapid prototyping, and so on. For such
>     programs, you want everyone to write the old
>
>     "use strict";
>
>
> In 10 years loading code via script tags will be an abandoned 
> practice, except for bootstrapping the module loader.

I will bet you real (beer) money you're wrong.

>  Look, "use strict" is the price that we have to pay for supporting a 
> legacy language, in a legacy loading context, into perpetuity.

No, you assert this but it does not prove itself and it's not an axiom.

* "use strict" was a way to shoe-horn pragma syntax into ES5 (originally 
ES3.1) under "no new syntax" as a restriction.

* It bites back because old browsers see nothing, yet it makes runtime 
semantic changes in new browsers, but time will heal this wound.

* It's an eyesore, which I think is a non-trivial objection.

* It is easy to leave out and hard to find when checking whether code in 
the middle of a function nest is strict.

Yes, it's standard, but so what? Having one means toward an end (strict 
code) does not mean we can't have other, more usable means over time. 
And again we seem to agree about module making some module bodies (only 
the out of line ones? I disagree but at least we're talking!) strict by 
fiat.

>  It's not going to be free.  Should we rather pay that balance by 
> creating sloppy modes for all future ES versions?  Is this the long 
> term vision, then?
>
>     ES3 < ES5 sloppy < ES6 sloppier < ES7 sloppiest
> < ES5 strict < ES6 strict < ES7 strict

I'm not sure to whom you are addressing this rhetorical question. ES6 
cannot mandate strict mode everywhere, so of course the default is 
sloppy. I'm the guy banging the drum for maximal (all new body-forms) 
strictness. That's the best we can do, for reasons recently rehashed in 
my reply to Claus Reinke on don't-break-the-web.

> What comes after ES7 sloppiest mode?

You're ginning up a fake argument here: no one is adding *more* implicit 
conversion nonsense (well, almost -- destructuring does an implicit 
ToObject on the RHS and Andreas Rossberg disagrees; I will post a 
separate thread on this, it's worth revisiting). No one is pushing new 
with-like forms. No one is arguing for more arguments object usage, let 
alone more aliasing _a la_ arguments[0] and first-formal named x.

Since we cannot mandate strict mode, we'll always have some sloppiness. 
Many on TC39 hope to provide new and better vegetables and fruits to 
lead the horses away from the bad old forms, and module is certainly 
sweet. Why stop there?

Note also that "use strict" cannot be extended arbitrarily with 
incompatible further strictness that governs existing forms, or we'll 
have more broken content tested only in old or new browsers.

We want ES5 strict mode to stay the same, and ES6 by default to be 
non-strict. This means new syntax could implicitly opt into strictness 
(as I advocate where there are code bodies, i.e., functions), or 
developers could opt in by composing "use strict" or module with new syntax.

But nothing here says we add more slop.

> To what extent does each new sloppy version have to drift from its 
> strict counterpart?  They cannot converge, so divergence is the only 
> possibility.

No one is making new slop that a stricter ES6 strict must ban. If we 
were, you'd have a point.

(Forthcoming post on destructuring addresses the deeper issue Andreas 
raises: future-proofing for pattern matching, vs. being "consistent" 
with old slop.)

> In this light, the least price for legacy support is to require "use 
> strict" in legacy contexts (e.g. script tags).

You are asserting again. "least price" sounds measurable. If we count 
chars, or measure actual error rates forgetting to add "use strict", or 
quantify any other way, I'm confident that requiring "use strict"; is 
not the least-cost or lowest-price path.

New syntax bearing a code body can be strict, and 1JS starting with 
Dave's proposal (in full) has at least 'module' strict by fiat, for both 
inline and out of line bodies.

This is least-price in terms of reading and writing. You could argue it 
is higher cognitive load, since one must check for two things, a 
governing "use strict"; or a  module ... { with closing } bracketing the 
code in question.

But again, "use strict" has worse usability and it's harder to find (no 
bracketing). In most editors one can set a mark, find the nearest 
preceding 'module', match the } for its opening {, and see by point vs. 
mark line number differences, coloring, or better visualizations that 
the code in question is bracketed.

If most modules are out of line, then it's even easier, and no ugly and 
easy-to-forget (or delete) "use strict"; at top is required.

I went through the inline-body module case because I think we will see 
inline bodies in the long run, a minority cohort for sure, but still 
useful. And I think the same delimiting/bracketing exercise should apply 
to classes at least (strict by default).

Granted, classes can be finer-grain and typically will be. Even moreso, 
generators and arrows. However, for new code strict should be the 
default, and usually it won't bite back. The biting has come when adding 
"use strict" at the "top" without carefully reviewing or rewriting all 
the code affected (and ignoring concatenation).

> Pull off the band-aid, bite the bullet, etc.  More harm is often done 
> by taking the slow road.  And ES6, with it's substantial syntax 
> improvements, is the best opportunity we'll have for getting it over with.

This is all kind of circular. You assert "use strict" is least-price and 
then make it the only way to get more strict code.

If by "pull off the band-aid" you mean get more strict code in the 
future than in the past, faster (more and faster over any measurable 
adoption interval), then making bodies strict by fiat wins.

> OK - so why not make all new code-bearing bodies implicitly strict then?

(Finally! :-P)

>  Won't that buy us more strict mode usage?  Well, sure, but the price 
> is added complexity.

Complexity is not a unitary evil. It exists in your proposal too, not 
just due to usability and aesthetic problems of "use strict", but also 
in the friction in favor of sloppy code you are creating (those users, 
the ones I set aside temporarily in my last message, who would use ES6 
but can't yet turn on strict mode at a high level).

Is the big fat future, with lots of modules, classes, arrows, and some 
generators, really more complex on balance, if those forms are strict? I 
don't think so.

>  It sounds simple enough, but when we start digging into the details, 
> little problems emerge (like "let").

No, you are mixing topics. 'let' is a problem for new syntax as its own 
opt-in (so was 'module' but we did the obvious contextual thing).

'let' is not a problem for new-bodies-are-strict-by-fiat.

Please keep these two issues separate.

>  It's already complex enough to support both strict and sloppy modes, 
> why compound that complexity by extending the reach of sloppy mode?

I'm not, I'm extending strict by design into new bodies.

> I think that pretty much sums up the position, and the horse may be 
> quite beaten at this point.  What do you think?

I think the two issues I argue should be kept separate (new bodies are 
strict vs. new syntax is its own opt-in) are connected in your mind by 
"complexity".

*If* we keep the ability to make 'let' new syntax be its own opt-in 
(which again, we think we can keep via some lookahead restrictions and 
left context, similar to what we do elsewhere in ES1-5 and ES6 for 
'module'), then there's no problem. But you could be right and we'll end 
up makin 'let' available only in strict mode (enabled by whatever means 
we choose).

Function-in-block faces a rougher road, and looks likely to precede 
'let' into strict-only status -- but Mark at least wants to evangelize, 
so we don't have two f-i-b semantics (only one of them specified). We 
knew it might not be possible to change f-i-b semantics in all modes to 
match ES6, in particular to scope the hoisted binding to the enclosing 
block. This may happen, looks likelier, but not a done deal either.

Your complexity argument is that by putting all new syntax (save 
'module') under a required "use strict" (or out-of-line module?), 
developers don't have to face certain complexity. I argue, to the 
contrary, that they face as much or worse. They have to:

* find the governing "use strict" for a given piece of code in a 
function nest;

* worry about module inline-to-out-of-line refactoring breaking 
pre-strict code that was put in the inline module in a prior refactoring 
step;

* keep using 'var' in code when in doubt about strictness, retarding 
'let' adoption.

* face old f-i-b code that has different semantics from new f-i-b, but 
this one can't be solved other than by the *end* of strictness, so it is 
not an argument for or against a particular *means* of enabling strict mode.

The last bullet is a hard case that won't go away in any scenario, in 
the worst case where we can't evangelize old content to new semantincs. 
Yet it doesn't crack your perfect dodecahedron vs. my different (and 
more usable) smooth-hand-fitted shape :-P. Why not?

/be


More information about the es-discuss mailing list