if-scoped let
Slight correction: bring in line with "for in" and "for of" only - since the condition part of the "for" does not allow this currently.
Nick Krempel wrote:
Slight correction: bring in line with "for in" and "for of" only - since the condition part of the "for" does not allow this currently.
Right, and for (let...;;)
has (consensus reconfirmed last meeting) a
fresh let binding per iteration (and one for the pre-loop scope if
there's a closure in the first part of the head that captures a let
binding -- turns out Dart does the same thing).
It's too late for ES6, but if (let ...)
, while (let ...)
, and switch (let ...)
seem unproblematic to consider for ES7. I like them, we've
talked about them more "off" than "on" over the years, I'm not sure why
they never gained a champion.
do {...} while (let ...);
is troublesome, though -- the condition is
at the bottom but the binding would be hoisted to the do
. Yes, it can
be made to work, but the return of hoisting, no TDZ, smells. I'd skip it.
I also miss these from C++, especially the if form. I also agree that we do not want the do-while form of this.
On 11/29/2013 08:29 AM, Nick Krempel wrote:
Couldn't find anything on this in the archives, but is there a proposal for:
if (let var = expr) { // var in scope } else { // var in scope } // var out of scope
I frequently use the C++ equivalent of this. Haven't proposed it as part of the scoping upgrades just to keep things small, but I don't have any good reason not to do something like this either. It kind of falls in the same bucket as &&=
, ||=
, and ^^
.
I mailed Arv and he kindly offered to draft and champion a bite-sized strawman for ES7, to support if/while/switch(let). Yay!
What's ^^ ?
On 12/03/2013 05:30 PM, Mark S. Miller wrote:
What's
^^
?
a^^b
would essentially be the same as !a!=!b
except that it would return the actual truthy value if it returns true.
I have to say if (let x = ... ) { /* that x in scope here */ }
is >>> ^^
, if you get what I mean :-P.
2013/11/29 Nick Krempel <ndkrempel at google.com>
Couldn't find anything on this in the archives, but is there a proposal for:
if (let var = expr) { // var in scope }
... ("const" should also be OK in place of "let", at least for "if" and "switch".)
Thanks for taking this to the list. I was meaning to do it after a discussion with Allen at Front-Trends earlier this year but never got around to it, partly because Allen (correctly) suggested that it was almost certainly too late for ES6. I back this also.
Another perspective of why this is a great feature: My ES6 programming is const-first, meaning I only use let for bindings that change and const for everything else. In practice over 90% of all my variables are const which is great because the let's that are in there really stand out. The unfortunate consequence of not being able to declare a variable inside the if-condition (for example) is that it forces const's to let's.
I wanted to do
if (const val = compute(something)) {
// ...
}
but I had to do
let val;
if (val = compute(something)) {
// ...
}
which is unfortunate not only because val
leaks to the outer scope but also
because let
suggest that the binding mutates (which it technically does,
but practically doesn't).
On 04 Dec 2013, at 9:13 , Olov Lassus <olov.lassus at gmail.com> wrote:
I wanted to do
if (const val = compute(something)) { // ... }
but I had to do
let val; if (val = compute(something)) { // ... }
which is unfortunate not only because val leaks to the outer scope but also because let suggest that the binding mutates (which it technically does, but practically doesn't).
I’m curious, why not:
const val = compute(something);
if (val) {
// ...
}
One thing that makes me skeptical w.r.t. if
: will this kind of in-statement declaration only be used for truthy/falsy tests? In loops (for-of, while), I find this kind of thing useful, for if-then-else, much less so.
On 4 December 2013 10:13, Olov Lassus <olov.lassus at gmail.com> wrote:
I wanted to do
if (const val = compute(something)) { // ... }
but I had to do
let val; if (val = compute(something)) { // ... }
which is unfortunate not only because val leaks to the outer scope but also because let suggest that the binding mutates (which it technically does, but practically doesn't).
I don't understand. Why can't you do
const val = compute(something)
if (val) {
// ...
}
instead? Or, if you also want to address the other half of your concern, use an additional block (which admittedly is more verbose).
Olov did write "because val leaks to the outer scope". That's a reason for the convenience of
if (let x = ...) { /* x in scope here */ }
(or const), vs.
{ let x = ...; if (x) { /* ... */ } }
Braces count, this is winning in C++.
Yes, see the last sentence of my reply. What I (and presumably Axel) was puzzled about is why Olov said that he was forced to use 'let' instead of 'const'.
I took him to mean "please support const or let" - for sure! :-)
To voice the con side, I'm not fond of binders in conditions because they depend on -- and thus encourage overuse of -- falsy/truthy values or other implicit-conversion-like techniques (and their metastasis in APIs). I have seen that harm overall clarity more than it helps, at least in C++.
Taking it further, a (probably controversial) suggestion would be to allow "let" and "const" to be expressions, enabling:
if ((let foo = getFoo()).isReady()) {
// foo in scope
} else {
// foo in scope
}
// foo not in scope
There would be some details about whether its value is a Reference and what to do with "let a = 1, b = 2" but probably the biggest issue would be parsing ambiguities?
Or in a different direction, could consider allowing ToBoolean conversions for objects to be overloaded.
Even without either of the two additions proposed above, I would still find the if/switch/while(let) construct useful. For example, often a value is known to be either an object or null (or undefined). (And if it had the value 0 due to a bug in my program, it's not clear whether I want the true branch or the false branch anyway: so I should just use an assert if I want to protect against this.)
I should also say that I agree with you that it is unfortunate that if getFoo() is a function returning either a number or undefined (say), you can't use the proposed if(let) syntax to distinguish undefined.
Nick Krempel wrote:
Taking it further, a (probably controversial) suggestion would be to allow "let" and "const" to be expressions, enabling:
if ((let foo = getFoo()).isReady()) { // foo in scope } else { // foo in scope } // foo not in scope
There would be some details about whether its value is a Reference and what to do with "let a = 1, b = 2" but probably the biggest issue would be parsing ambiguities?
Yes, because let is not reserved in ES1-3, or ES5 non-strict, we can't recognize it in expressions, only at start of statement (and even then we propose to break any old code of the form let[x] = y; by interpreting that as a destructuring let binding using a single-element array pattern).
ES4 had let expressions (let (x = y, z = w) ...), but presumed opt-in versioning, thankfully dead with 1JS and never to return.
Or in a different direction, could consider allowing ToBoolean conversions for objects to be overloaded.
Best not to overload your proposals, first.
Also, we do not want ToBoolean overloaded on arbitrary objects. Value objects as I've shown in several talks, e.g.,
www.slideshare.net/BrendanEich/js-resp
do allow boolean test to be customized, but only for a value class
Nick Krempel wrote:
I should also say that I agree with you that it is unfortunate that if getFoo() is a function returning either a number or undefined (say), you can't use the proposed if(let) syntax to distinguish undefined.
Then you would have to write it out the long way, and use a block around the if to narrow the scope. No shorthand proposal can satisfy all use-cases, but I think it would be a mistake to add if(let) and not test truthiness.
Of course, we may not agree to add such a truthy-testing if(let). But let's have Arv's strawman fiirst!
2013/12/4 Andreas Rossberg <rossberg at google.com>
I don't understand. Why can't you do
const val = compute(something) if (val) { // ... }
(also Axel) Oops - yeah I sure could. consts are one honking great idea -- let's do more of those! That's all. :)
On Tue, Dec 3, 2013 at 9:13 PM, Waldemar Horwat <waldemar at google.com> wrote:
a^^b
would essentially be the same as!a!=!b
except that it would return the actual truthy value if it returns true.
Those semantics are extremely error-prone. (("foo" ^^ "bar") ^^ "baz") !== ("foo" ^^ ("bar" ^^ "baz"))
, and I doubt anyone will remember which
corresponds to "foo" ^^ "bar" ^^ "baz"
?
Also, the syntax gives the impression that this is a short-circuiting
operator since it is a "doubled bitwise operator" like ||
and &&
, but it
can't be short-circuiting.
Yup. This is way, Way lower priority than if(let|const).
Wouldn't waiting for es7 make this a breaking change?
Nm. It would be illegal in the current spec.
Couldn't find anything on this in the archives, but is there a proposal for:
if (let var = expr) { // var in scope } else { // var in scope } // var out of scope
i.e. shorthand for:
{ let var = expr; if (var) { // ... } else { // ... } }
Also:
switch (let var = expr) { case foo: // var in scope default: // var in scope } // var out of scope
...which has a similar expansion.
Similarly for "while" and "do...while": this would bring everything in line with the current "for" / "for in" / "for of".
This also matches what C++ allows, with the exception of "do...while" (in our case, it seems acceptable to allow "do...while" too - the value would simply be "undefined" on the first iteration).
("const" should also be OK in place of "let", at least for "if" and "switch".)