Toplevel 'let' binding can be left permanently uninitialized after an error

David Herman dherman at
Tue Sep 30 13:38:31 PDT 2014

On Sep 30, 2014, at 4:03 AM, Andreas Rossberg <rossberg at> wrote:

> On 30 September 2014 12:52, Jason Orendorff <jason.orendorff at> wrote:
>> I just realized this has an unfortunate implication for REPLs. Suppose
>> you make this typo:
>>    js> let x = Math.cso(a)    // oops, TypeError, should be Math.cos
>> Now x is irreparably hosed in your REPL. That seems bad.
> No surprise. One of the reasons why I always favoured a nested scopes
> model for multiple scripts...

We've had this debate a million times, but for anyone who's unfamiliar with the arguments: nested scopes makes recursive bindings across scripts impossible and is also hard to understand with things like asynchronous script execution order (such as <script async>). And it's totally different from the existing mental model of global code.

But since we're rehashing, this is an example of why *I've* always thought new binding forms should just go in the global object just like vars and functions do. JS's global scope is extremely complex and subtle, and you can't undo that complexity by adding additional layers. And the metal model, while unfortunate, already occupies programmers' brainspace -- adding an extra layer of scope for some binding forms makes the mental model more complex.

We basically have the following set of constraints that various people have tossed into the mixing bowl. It seems no matter which way you mix it, at least one of the constraints falls back out of the bowl:

1. Make `let` mean the same thing at top-level that it means locally.
2. Enable recursive bindings across multiple scripts.
3. Avoid the spec/design work of reifying new binding forms' reification in the global object.
4. Preserve the mental model of the global object.

Note that, as so often happens, we have one dimension of consistency (scoping semantics of `let` in local vs. global contexts) in tension with another dimension of consistency (globals going in the global object vs. globals going in a new scope layer).

I'm still convinced we made the wrong call, i.e., we chose the wrong dimension of consistency. The global scope was always a mess and we aren't going to make it less of a mess no matter what we do. We favored a philosophical consistency over a historical consistency, with predictable results. But it is what it is. We've talked before about exposing a reflection of the extra global layer, maybe via the Reflect API but certainly through the Realm API. We may need to add controls to that to allow undoing these kinds of annoyances Jason brings up.

I'm usually less concerned about REPLs, since they can decide for themselves what kind of context they want to execute in -- or even invent new non-standard non-terminals, frankly -- although in this case it's not quite clear what a let declaration *should* do in the REPL. Maybe developer consoles should actually treat `let` the way IMO we should've done for scripts. :)


More information about the es-discuss mailing list