Uniform block scoping

Allen Wirfs-Brock allen at wirfs-brock.com
Wed Jul 16 09:38:35 PDT 2014


(Note that this topic is on the agenda for this month's TC39 meeting)
(Also, note that as far as I can tell, the disagreement is only about the early errors described below. The use of a separate parameter scope that limits what closures in parameter expressions can capture  has been agreed upon and is already in the ES6 draft spec.)

Everything would be so much cleaner if all we had were strictly lexically scoped declarations and no legacy to deal with...

Consider,

We've decided that that we don't want to allow multiple declarations for the same name in a scope:

function f() {
    let x;
    const x;  //early redeclaration error
}

function f() {
  let x;
  var x;  //early redeclaration error
}

function f() {
    let x;
    function x() {};  //early redeclaration error
}

We don't even have a runtime semantics for any of the above duplicate declarations.

Except that for legacy reasons we have to allow:
function f() {
    var x;
    function x() {}; 
    function x() {};  
    var x;
}
using the legacy ES semantics. 

The legacy ES semantics also requires that:

function f(x) {
   var x;   //not an error
   console.log(x);
}
 f(1);  //logs 1, not undefined

just like:

function g(x) {
   console.log(x);
}
g(1);  //logs 1

In other words, from the perspective of  the body of the function, the following two declarations appear to be equivalent:
function f(x) {
}
function f(x) {
   var x;  
}

But we've already established this is an error:
function f(x) {
   var x;
   let x;  //early error, duplicate definition
}

and if that is an error, then its equivalent alternative form should also be an error:
function f(x) {
   let x;  //early error, duplicate definition
}

And that is the crux of the disagreement. 

Andreas would like to reason about ES scoping as simple nested block contours with shadowing.  But the reality  is more complex than that.  We have vars declarations the hoist to the top level.  We have interactions between formal parameters and var declarations that interact with outer scopes (that are inner relative to the parameters if you think of the parameter list is a distinct scope contour).  In fact, in my current working draft, FunctionDeclarationInstantiation is almost 3 pages of detailed algorithmic specification. (and adding a parameter scope didn't simplify things...).

We really don't want the average (and certainly not the novice) ES programmer to have to understand all the technical subtleties of these interactions. It's much easer to have a few simple rules that states which declarations are legal and which declarations are not.  The rules are:

   It is illegal  for let/const/class declarations at the top level of a function to multiply define the same name.
   It is illegal  for a let/const/class declaration at the top level of a function to define the same name as a top level function declaration. 
   It is illegal  for a let/const/class declaration at the top level of a function to define the same name as a var declaration that occurs anywhere within the function body.
   It is illegal  for a let/const/class declaration at the top level of a function to define the same name as a formal parameter of the function.

This is what the ES6 spec. currently says (using slightly different words). Andreas would like to eliminate that lat rule. I think it should remain, both for the specific equivalence discussed above and for overall simplicity.  

> In terms of spec, this simply amounts to dropping several bullet
> points imposing syntactic restrictions about LexicallyDeclaredNames
> [2] -- i.e., simplification :). (Note: The legacy rule that legal ES5
> function declarations are var-like, not lexical, would of course be
> unaffected.)

It's actually not quite that simple.  There are a number of places where the specified runtime semantics are simplified because it is known that early error rules have eliminated various possibilities, such as parameter/let naming conflicts.  So, if we made this changeI will need to review all the relevant runtime semantics and look for newly exposed cases that need to be handled. 

More on catch clauses and catch parameter scoping in a seperate message. 

Allen


More information about the es-discuss mailing list