restrictions on let declarations

# John Lenz (10 years ago)

I have some old notes that says that let can't be used in some context where a var could like:

if (a) let x = 2;

In my perusal of the spec I don't see that this is the case now. Can someone confirm that for me?

# Erik Arvidsson (10 years ago)

It falls out of the grammar.

IfStatement can only contain Statement which does not include Declaration without going through a BlockStatement.

# John Lenz (10 years ago)

It seems unfortunate that let and const have different usage rules from var. It seem strange that var is considered a statement and not declaration as per:

people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-language-statements-and-declarations

Generally, I've always thought of:

if (x) ... as equivalent to if (x) { ... }

Does this restriction on let/const enable anything?

# Brendan Eich (10 years ago)

John Lenz wrote:

Generally, I've always thought of:

if (x) ... as equivalent to if (x) { ... }

let and const (and class) are block-scoped. {...} in your "if (x) {...}" is a block. An unbraced consequent is not a block, and you can't have a "conditional let binding".

The restriction avoids nonsense such as

let x = 0; { if (y) let x = 42; alert(x); }

What pray tell is going on here, in your model?

# John Barton (10 years ago)

I'm with John: the alert should say 0 and I can't see why that is not obvious.

# Allen Wirfs-Brock (10 years ago)

On Jan 30, 2014, at 7:43 AM, John Lenz wrote:

It seems unfortunate that let and const have different usage rules from var. It seem strange that var is considered a statement and not declaration as per:

people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-language-statements-and-declarations

Generally, I've always thought of:

if (x) ... as equivalent to if (x) { ... }

Does this restriction on let/const enable anything?

The anomaly is that var was considered a statement in ES1. But note that the only other declaration in the language (function) at that time was not a statement. See www.wirfs-brock.com/allen/draft-ES5.1/#sec-14

Declarations have global static impact within their containing scope (function or block). It generally doesn't make any sense to use them in a conditional context without an explicitly surrounding block that constrains their scope.

So all new declarative forms in ES6 are treated similarly to function. They are declarations but not statements. var must remain a statement for legacy compatability.

# Dean Landolt (10 years ago)

On Thu, Jan 30, 2014 at 10:59 AM, John Barton <johnjbarton at google.com>wrote:

I'm with John: the alert should say 0 and I can't see why that is not obvious.

It's not obvious at all -- what happens when you drop the initial let x = 0; and you just have { if (y) let x = 42; alert(x); } -- now what happens? Is x declared or not?

To my mind if (y) let x = 42; reads like it's own 1-line noop block -- at least, that's what I'd expect of the scope. So while it could be allowed in that sense, it'd only serve as a footgun when y is true.

# Brendan Eich (10 years ago)

John Barton wrote:

I'm with John: the alert should say 0 and I can't see why that is not obvious.

Interesting!

You don't want the alert to show undefined, so the extent of the inner binding in your model is the unbraced consequent of the "if".

That is not "block scope" in any plain sense.

# Oliver Hunt (10 years ago)

On Jan 30, 2014, at 8:07 AM, Dean Landolt <dean at deanlandolt.com> wrote:

It's not obvious at all -- what happens when you drop the initial let x = 0; and you just have { if (y) let x = 42; alert(x); } -- now what happens? Is x declared or not?

To my mind if (y) let x = 42; reads like it's own 1-line noop block -- at least, that's what I'd expect of the scope. So while it could be allowed in that sense, it'd only serve as a footgun when y is true.

This is exactly foot gun the language restriction is intended to avoid.

Most modern [Obj-]C[++] will warn on this (well, s/let/int/)

You might be getting confused because of the bizarro var hoisting semantics of var

if (y) let x = “nope"; alert(x)

Results in an unusable binding of x, and so this would throw (the foot gun occurs if you’re shadowing x, that’s another shadow that i think C compilers will warn on), e.g..

y = true; let x = “whoops”; if (y) let x = “nope"; alert(x) // “whoops"

The var case

y = true; var x = “whoops”; if (y) var x = “nope"; alert(x); // “nope"

is actually interpreted as

var x; y= true; x = “whoops”; if (y) x = “nope”; alert(x); // “nope"

That craziness is the whole point of block scoping let. More interestingly

if (window.SomeCrazyDomFeature) var foo = true;

is a common web idiom as it brings foo into scope for everything, so makes later "if (foo) “ statements safe. Anyone trying to do this with a |let| would get incorrect (from their PoV) behaviour. Again that’s why we error out.

Give that this is the behaviour of every other block scoped language i don’t see why this is confusing.

# John Lenz (10 years ago)

In my model

if (y) let x = 42

is equivalent to

if (y) { let x = 42 }

and it is clear "x" in "alert(x)" is 0;

# John Barton (10 years ago)

On Thu, Jan 30, 2014 at 10:59 AM, John Barton

That craziness is the whole point of block scoping let. ...

Give that this is the behaviour of every other block scoped language i don’t see why this is confusing.

It's not let vs var that concerns us, it's that the statement following an if condition is not equivalent to a block.

jjb

# Jason Orendorff (10 years ago)

On Thu, Jan 30, 2014 at 8:26 AM, John Lenz <concavelenz at gmail.com> wrote:

In my model

if (y) let x = 42

is equivalent to

if (y) { let x = 42 }

Yes, everyone here understands your model. This is a question of taste. There are competing senses of what is intuitive at play.

# John Lenz (10 years ago)

How did "let x" in for-loops land:

for (let x = 1; x < 10 ; i++) { // is "x" a fresh binding for every iteration? }

This wouldn't be "block" scoping either.

# Rick Waldron (10 years ago)

On Thu, Jan 30, 2014 at 11:39 AM, John Lenz <concavelenz at gmail.com> wrote:

How did "let x" in for-loops land:

for (let x = 1; x < 10 ; i++) { // is "x" a fresh binding for every iteration?

Yes.

}

This wouldn't be "block" scoping either.

See: rwaldron/tc39-notes/blob/master/es6/2013-11/nov-20.md#consensusresolution

# Brendan Eich (10 years ago)

John Lenz wrote:

In my model

if (y) let x = 42

is equivalent to

if (y) { let x = 42 }

and it is clear "x" in "alert(x)" is 0;

It is as clear as those invisible braces in your model's input :-P.

Come on, this is silly. "var" has quirks, but we are not propagating them to other forms, making implicit blocks where no braces exist, etc.

# Brendan Eich (10 years ago)

John Lenz wrote:

How did "let x" in for-loops land:

for (let x = 1; x < 10 ; i++) { // is "x" a fresh binding for every iteration? }

This wouldn't be "block" scoping either.

It is -- special forms that have heads can bind in bodies. We see this with formal parameters to functions, also with the defunct let blocks and let expressions of ES4. ML has similar forms.

We made this block scoping, and how! Turns out Dart did the same. Each iteration gets a fresh binding. If there's a closure in the first part of the for(;;) head that captures the loop variable, it gets a "0th iteration" binding.

# Rick Waldron (10 years ago)

On Thu, Jan 30, 2014 at 12:10 PM, Brendan Eich <brendan at mozilla.com> wrote:

It is -- special forms that have heads can bind in bodies. We see this with formal parameters to functions, also with the defunct let blocks and let expressions of ES4. ML has similar forms.

We made this block scoping, and how! Turns out Dart did the same. Each iteration gets a fresh binding. If there's a closure in the first part of the for(;;) head that captures the loop variable, it gets a "0th iteration" binding.

Also, there was very positive interest in Waldemar's proposed if-scoped let, here: esdiscuss/2013-December/035077

# Brendan Eich (10 years ago)

Rick Waldron wrote:

Also, there was very positive interest in Waldemar's proposed if-scoped let, here: esdiscuss/2013-December/035077

Nick Krempel proposed that upthread, just for the record.

Yeah, it's good. It comes from C++ but goes back to Algol, IIRC. Another precedent for block-scoped bindings that are declared in heads.

# John Barton (10 years ago)

On Thu, Jan 30, 2014 at 8:59 AM, Brendan Eich <brendan at mozilla.com> wrote:

It is as clear as those invisible braces in your model's input :-P.

Come on, this is silly.

Not silly. Can you suggest any on-line that most JS developers can understand discussing how these two forms differ?

Here are some that describe them as equivalent:

and so on across multiple languages. Whether or not you think these forms should be different, programmers don't expect them to differ.

# Rick Waldron (10 years ago)

On Thu, Jan 30, 2014 at 12:24 PM, Brendan Eich <brendan at mozilla.com> wrote:

Nick Krempel proposed that upthread, just for the record.

Yes, my mistake--I had looked in December archives and found what I thought was the OP. This is the correct OP from November:

esdiscuss/2013-November/035060

# Brendan Eich (10 years ago)

John Barton wrote:

Not silly. Can you suggest any on-line that most JS developers can understand discussing how these two forms differ?

Here are some that describe them as equivalent:

en.wikipedia.org/wiki/JavaScript_syntax#If_..._else msdn.microsoft.com/en-us/library/kw1tezhk(v=vs.94).aspx, msdn.microsoft.com/en-us/library/kw1tezhk(v=vs.94).aspx, programmers.stackexchange.com/questions/16528/single-statement-if-block-braces-or-no, publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp?topic=%2Fcom.ibm.vacpp7a.doc%2Flanguage%2Fref%2Fclrc08csor.htm, msdn.microsoft.com/en-us/library/ms173143.aspx

and so on across multiple languages. Whether or not you think these forms should be different, programmers don't expect them to differ.

This is all beside the point, since the unbraced let as consequent of if cannot make a useful binding (if you use comma-separated multiple declarators, you still can't use any of the values that initialize the bindings except in later useless-outside-the-single-declaration consequent).

# John Lenz (10 years ago)

I don't argue that it isn't a useless "let". I do point out that in "sloppy" mode, that other declaration are allow in practices by browsers. Chrome allows function, const, and var in the body of an if without block.

It does seems like an unnecessary restriction, and with it I'll need to make sure that Closure Compiler doesn't introduce these when stripping blocks, not a big deal just one more thing to deal with.

I was just hoping that the restriction was enabling something and not just noise.

# Oliver Hunt (10 years ago)

On Jan 30, 2014, at 11:27 AM, John Lenz <concavelenz at gmail.com> wrote:

I don't argue that it isn't a useless "let". I do point out that in "sloppy" mode, that other declaration are allow in practices by browsers. Chrome allows function, const, and var in the body of an if without block.

That’s because they have to be allowed as these constructs are used on millions (billions?) of web pages. Disallowing that behaviour would break the web and we can’t do that.

Introducing new constructs that expose the same coding problems is not worth it - maybe we would want to change something in the future but we would already have been burned by existing content using this code incorrectly.

—Oliver

# Rick Waldron (10 years ago)

On Thu, Jan 30, 2014 at 2:27 PM, John Lenz <concavelenz at gmail.com> wrote:

I don't argue that it isn't a useless "let". I do point out that in "sloppy" mode, that other declaration are allow in practices by browsers. Chrome allows function, const, and var in the body of an if without block.

Did you mean to include const? Only IE11 has implemented const and let with the proper semantics (with the exception of the for loop let binding mistake, which will be corrected).

# John Lenz (10 years ago)

Yes, that was my point, that is has to be allowed currently and this is a change. Using what incorrectly? Existing "const" and "function" implementations? Or if "scoped" declarations?

# Rick Waldron (10 years ago)

On Thu, Jan 30, 2014 at 2:59 PM, John Lenz <concavelenz at gmail.com> wrote:

Yes, that was my point, that is has to be allowed currently and this is a change.

But let and const aren't the same as var and function, they have different syntax with different static and runtime semantics. New forms are allowed to have new rules, especially ones that make writing code with the new forms more intuitive and less error prone.

# Brendan Eich (10 years ago)

John Lenz wrote:

I don't argue that it isn't a useless "let". I do point out that in "sloppy" mode, that other declaration are allow in practices by browsers. Chrome allows function, const, and var in the body of an if without block.

But those aren't useless.

They are quirky, but they do not bind in the consequent only, which is exactly what you and John said you wanted for if(x)let y=z.

# Thaddee Tyl (10 years ago)

On Thu, Jan 30, 2014 at 8:27 PM, John Lenz <concavelenz at gmail.com> wrote:

I don't argue that it isn't a useless "let".

For the sake of argument, can you construct a non-block-scoped if (…) … which is actually any useful, assuming your mental model of

if (…) … <==> if (…) { … }

(which must be removed from teaching material) applies?

By the way, even in the current specification of let, we can still construct a useless let:

for (let x = 42;false;) console.log(x);

(which is the same as the example you gave, using "if").

# Brendan Eich (10 years ago)

Thaddee Tyl wrote:

By the way, even in the current specification of let, we can still construct a useless let:

 for (let x = 42;false;) console.log(x);

(which is the same as the example you gave, using "if").

Yes, this falls out of the JS (and Dart, convergent evolution, coincidental as far as I can tell) design, but so what? OMG LETS MAKE USELESS LETS EVERYWHERE LOLJSSUXZ0RZ! Um, no.

# Jorge Chamorro (10 years ago)

How about this?

let x= 0;
if (1) eval("let x= 42; alert(x);"); //Is this in its own block?
alert(x);

On 31/01/2014, at 03:11, Brendan Eich wrote:

OMG LETS MAKE USELESS LETS EVERYWHERE LOLJSSUXZ0RZ! Um, no.

:-)

# André Bargull (10 years ago)

eval() hasn't yet been updated to work with the new lexical declaration forms, but I hope the example from above will be evaluated in a new block. See ecmascript#1788 for a related bug report.