On dropping @names

David Herman dherman at mozilla.com
Thu Dec 27 12:23:24 PST 2012


On Dec 27, 2012, at 1:51 AM, Andreas Rossberg <rossberg at google.com> wrote:

> I think hoisting can mean different things, which kind of makes this debate a bit confused.

Yep. Sometimes people mean "the scope extends to a region before the syntactic position where the declaration appears," sometimes they mean "the scope extends to the function body," and sometimes they mean "function declaration bindings are dynamically initialized before the containing function body or script begins executing."

> There is var-style hoisting. Contrary to what Rick said, I don't think anybody can seriously defend that as an "excellent" feature. First, because it hoists over binders, but also second, because it allows access to an uninitialized variable without causing an error (and this being bad is where Dave seems to disagree).

Are you implying that my arguments are not serious? :-(

> Then there is the other kind of "hoisting" that merely defines what the lexical scope of a declaration is. The reason we need this backwards-extended scope is because we do not have an explicit let-rec or something similar that would allow expressing mutual recursion otherwise -- as you mention. But it does by no means imply that the uninitialized binding has to be (or should be) accessible.

No, it doesn't. I'm not interested in arguments about the "one true way" of programming languages. I think both designs are perfectly defensible. All things being equal, I'd prefer to have my bugs caught for me. But in some design contexts, you might not want to incur the dynamic cost of the read(/write) barriers -- for example, a Scheme implementation might not be willing/able to perform the same kinds of optimizations that JS engines do. In our context, I think the feedback we're getting is that the cost is either negligible or optimizable, so hopefully that isn't an issue.

But the other issue, which I worry you dismiss too casually, is that of precedent in the language you're evolving. We aren't designing ES1 in 1995, we're designing ES6 in 2012 (soon to be 2013, yikes!). People use the features they have available to them. Even if the vast majority of read-before-initialization cases are bugs, if there are some cases where people actually have functioning programs or idioms that will cease to work, they'll turn on `let`.

So here's one example: variable declarations at the bottom. I certainly don't use it, but do others use it? I don't know.

>> - It automatically makes forward references work, so you can:
>> * order your definitions however it best "tells the story of your code," rather than being forced to topologically sort them by scope dependency
>> * use (mutual) recursion
> 
> Right, but that is perfectly well supported, and more safely so, with TDZ.

My point here was just about hoisting (perhaps a bit OT, but the question came up whether hoisting is bad) -- specifically, of having declarations bind variables in a scope that extends to a surrounding region that can cover expressions that occur syntactically earlier than the declaration itself. TDZ is orthogonal.

>> - It binds variables without any rightward drift, unlike functional programming languages.
> 
> I totally don't get that point. Why would a rightward drift be inherent to declarations in "functional programming languages" (which ones, anyway?).

Scheme:

    (let ([sq (* x x)])
      (printf "sq: ~a~n" sq)
      (let ([y (/ sq 2)])
        (printf "y: ~a~n" y)))

ML:

    let sq = x * x in
      print ("sq: " ^ (toString sq) ^ "\n");
      let y = sq / 2 in
        print ("y: " ^ (toString y) ^ "\n")

ES6:

    let sq = x * x;
    console.log("sq: " + sq);
    let y = sq / 2;
    console.log("y: " + y);

Obviously functional programming languages can do similar things to what ES6 does here; I'm not saying "functional programming sucks." You know me. :)

Dave



More information about the es-discuss mailing list