Jan 18 meeting notes

Waldemar Horwat
Wed Jan 18 17:27:30 PST 2012
My rough notes from today's meeting.


DaveH: One JavaScript (Versioning debate)
It's inevitable (and necessary) that ES6 will have some breaking changes
around the edges.  How to opt-in?
DaveH's versioning proposal: A module block or file include is the only
in-language ES6 opt-in.  Modules can appear only at the top level or inside
another module.  This avoids the problem of a "use strict" nested in a
function inside a with.

var obj = get_random_obj();
var x, prop = 42
with (obj) {
  x = function() {
    "use strict";
    return prop;

Differences between the de facto (traditional) semantics and ES6 (i.e.
semantic changes instead of mere syntax additions):
- ES5 strict changes
- static scoping (really means static checking of variable existence; see
- block-local functions
- block-local const declarations
- tail calls  (yikes - it's a breaking change due to Function.caller)
- typeof null
- completion reform
- let
DaveH: Thinks we may be able to get away with enabling completion reform
and let for all code.

Allen: Would a class be allowed outside a module?
DaveH: Yes, but it would not support static scoping, block-local functions,
MarkM: Classes should not be allowed in traditional semantics.  If you want
a class, you need a "use strict" or be inside a module.

Waldemar: Given that you can't split a module into multiple script blocks,
making modules be the only in-language opt-in is untenable.  Programmers
shouldn't have to be forced to use the broken scope/local function/etc.
semantics to split a script into multiple script blocks.
DaveH: Use out-of-language opt-ins.

MarkM: Wants a two-way fork (non-strict vs. strict) instead of a three-way
fork (non-strict vs. strict vs. ES6-in-module).
MarkM: Does a failed assignment inside a non-strict module throw?

DaveH: Most of the differences between strict and non-strict are code bugs.
Luke, MarkM: No.  Their developer colleague experience shows that there are
plenty of changes to non-buggy code that need to be made to make it work
under strict mode.

Allen, Waldemar: It's important to support the use case of someone writing
global code using the clean new semantics and not having to learn about the
obsolete traditional compatibility semantics.

Can "use strict" be the ES6 opt-in?

What DaveH meant by static scoping (i.e. static checking):
What happens when there's a free variable in a function?

Nonstrict ES5 code:
- Reference error if variable doesn't exist at the time it is read; creates
a global if doesn't exist at the time it is written.

Strict ES5 code:
- Reference error if variable doesn't exist at the time it is read or

Static checking goal for ES6 modules:
- Compilation error if variable doesn't exist at the time module is
- Reference error if variable doesn't exist at the time it is read or
(It's possible to get the latter error and yet have the module compile
successfully if someone deletes a global variable outside the module
between when the module is compiled and when the variable is read or
written at run time.)

Discussion of whether it is important to support non-statically-checked
binding in modules.

MarkM: typeof is used to test for the existence of globals.  If the test
succeeds, they then proceed to use the global directly.  This would then be
rejected by static checks.

DaveH: Doesn't see a way to do static checking with strict code (due to,
for example, the "with" case illustrated by Brendan earlier).

MarkM: The cost of having three modes is higher than the cost of not
supporting static checking early errors.

DaveH's new proposal:  Other than static checking, attach the incompatible
ES6 semantics to the strict mode opt-in.  These semantics are
upwards-compatible with ES5 strict mode (but not ES5 non-strict mode).  The
semantics inside a module would be the strict semantics plus static

Do we want other new ES6 syntax normatively backported to work in
non-strict mode?
Waldemar, MarkM:  Not really.  This requires everyone to be a language
lawyer because it's introducing a very subtle new mode:  ES6 with nonstrict
scoping/const/local semantics.  If an implementation wants to backport, the
existing Chapter 16 exemption already allows it.
DaveH, Brendan:  Yes.  People won't write "use strict".  Don't want to
punish people for not opting in.
Alex:  Split the middle.  Backport new ES6 features to non-strict features
where it makes sense.

Waldemar, DaveH:  Want to make it as easiy as possible to make a strict
opt-in for an entire page instead of littering opt-ins inside each script.

Allen:  Backporting increases spec complexity and users' mental tax.  The
main costs are in making lots of divergent scoping legacy issues possible.

Doug:  Modules are sufficient as an opt-in, without the need for a "use
strict" opt-in.
Waldemar:  No.  Having multiple scripts on a page would require each one to
create its own module, and then issues arise when they want to talk to each
other -- you'd need to explicitly export const bindings, etc.
MarkM:  No.  The typeof test won't work.  Also, this would make it
impossible to write code that's backwards compatible with older browsers
that don't implement modules.

Which ES6 features can be backported into non-strict mode?
(blank: no significant issues;
 ?: possible issues;
 x: semantics conflict with de facto web)

? let  (syntax issues)
x const  (divergent semantics)
x function in block  (divergent semantics)
? destructuring  (syntactic conflict with array lookup)
  parameter default values
  rest parameters
x tail calls  (because of Function.caller)
  direct proxies
  simple maps and sets
  weak maps
  is / isnt (egal)
? generators  (interaction with scoping issues and yield keyword)
  generator expressions
  private names
  pragmas  (controversial)
? completion reform  (Brendan: might be able to get away with it without
breaking the web, but we don't know yet)
x typeof null  (Erik: It breaks the web)
n/a modules
  methods in object literals
  ({[computed_name]: value})  (MarkM: what should happen with duplicate
names in nonstrict mode?)

Brendan: Kill typeof null.  Replace it with Ojbect.isObject?

How are the names introduced by generators and classes scoped in nonstrict

MarkM: Example of code that might accidentally work one way in all current
browsers but not in ES6:
(another foo is also predefined in an outer scope.)
if (...) {
  function foo() {...}
} else {
  function foo() {...}
// foo will call one of the two foo's defined above in ES5 nonstrict,
although it's implementation-dependent which.  On some browsers the first
definition wins; on some the last definition wins; on some the one which
corresponds to the true branch of the if wins.  In ES6 strict it will call
the outer scope foo.

Discussion about whether we can move the web to the new local function and
const semantics even in nonstrict ES5 mode.  Also discussed an alternative
of whether we can require nonstrict mode to support limited usage scenarios
such as having the following work:
if (...) {
  function foo() {...}
Waldemar: This doesn't work in current ES5 non-strict if the if is inside a
with statement because an existing implementation might hoist foo across
the with and then foo() could refer to a field of the with'd object.  This
also might not work in the presence of other definitions of foo inside the
same function.

Not clear if specifying such limited cases in the normative spec is useful.

Current tentative decision is to support let, const, and local functions in
nonstrict ES5 in the same way as in strict ES6.  Fallback to either
specifying limited cases or doing the ES5 nonstrict status quo (i.e. syntax
error + Clause 16) if experiments show this to not be viable.  We won't
resolve this discussion without running some experiments.

Tail calls:
Luke: Remove them altogether.
Waldemar: If we support them only in strict mode, the failure mode is
someone copying-and-pasting code from a module to the global level and
silently losing tail recursion, leading to very confusing behavior.
Waldemar: We can require tail calls in non-strict mode by taking advantage
of the fact that Function.caller only remembers the last invocation of each
function.  Thus we can do an amortized algorithm analysis that allocates
the cost of storage of one stack frame link at the time we create the
function.  This makes it possible to implement tail calls in non-strict
mode while supporting Function.caller.
Tentative decision is to support tail calls in strict mode only.

Resolved:  Named generators behave in non-strict mode the same as in strict
mode.  "yield" is a contextual keyword in non-strict generators.

How to resolve let[x] = 5 in nonstrict mode?  Will need to do experiments.
Also, do we require no-line-terminator between "let" and the identifier?
Probably not, because if we do then we'd get this annoying hazard in
non-strict mode:
  let x = 7;
  if (...) {
      x = 5;
  // Now x is 5 because the second "let" is just a useless identifier
expression with an inserted semicolon, followed by an assignment to the
existing x!

Gavin: Module syntax hazard if we have no-line-terminator between "module"
and the identifier:
gets interpreted as a useless expression (the identifier "module") followed
by a block.

Completion value reform: Let's experiment.

Octal constants:
Useful as arguments to chmod.
Proposal for 0o123 (as well as 0b01110).
MarkM: Concerned about 0O123.
Waldemar: Nothing unusul here. We've lived with 36l (meaning 36 long
instead of 361) in Java and C++ for a long time.
Alternative for octal: 8r123 (but then we'd also want 16r123, 2r0101, and
maybe more).
Decided to allow 0o and 0b.  Unresolved whether to allow 0O and 0B.
Persistent weak feelings on both sides on the upper case forms.

Use __proto__ in object literals to do a put (assuming that a __proto__
getter/setter was created in Object.prototype) instead of a
defineProperty?  All modes or only nonstrict mode?
Allen: Make such use of __proto__ to be a synonym for <|.  If a <| is
already present, it's an error.
DaveH: __proto__ is ugly.  Don't want it in the language forever.
Waldemar: What about indirect [] expressions that evaluate to "__proto__"?
In Firefox they evaluate to accesses that climb the prototype chain and
usually reach a magic getter/setter-that-isn't-a-getter-setter named
__proto__ that sits on Object.prototype.
MarkM: Likes the ability to delete __proto__ setter and thereby prevent
anything in the frame from mutating prototypes.
Waldemar: How do you guard against cross-frame prototype mutations?
DaveH: __proto__ is in the "omg, what were we thinking" category.
Waldemar: Opposed to making __proto__ mutate prototypes other than at
object construction.  This is getting insanely complex.

More information about the es-discuss mailing list