restrictions on let declarations
It falls out of the grammar.
IfStatement can only contain Statement which does not include Declaration without going through a BlockStatement.
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?
John Lenz wrote:
Generally, I've always thought of:
if (x) ...
as equivalent toif (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?
I'm with John: the alert should say 0 and I can't see why that is not obvious.
On Jan 30, 2014, at 7:43 AM, John Lenz wrote:
It seems unfortunate that
let
andconst
have different usage rules fromvar
. It seem strange thatvar
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 toif (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.
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.
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.
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.
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;
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
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.
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.
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
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.
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.
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
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.
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:
- en.wikipedia.org/wiki/JavaScript_syntax#If_..._else
- 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.
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:
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).
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.
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
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).
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?
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.
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.
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").
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.
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.
:-)
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.
I have some old notes that says that
let
can't be used in some context where avar
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?