Proposal: anaphoric if and while syntax

# Danielle McLean (8 years ago)

In current ECMAScript, it is legal to place a variable declaration inside the initialiser of a for loop, as well as to declare the variable used by a for...in or for...of loop within the declaring expression:

for (let i = 0; i < 5; ++i) console.log(i);
for (let item of collection) process(item);

When this syntax is used with let or const, the resulting variable is scoped to the loop and is not visible to the rest of the surrounding block.

I propose that this syntax be extended, making it legal to place a variable declaration within the condition of an if or while statement. Any truthy value will cause the if block to run or while loop to repeat, as usual - the advantage is that the particular truthy value is bound to a variable and can be used inside the conditional block. For example, here is the situation that prompted my writing this proposal:

if (const oldValue = _.get(object, 'some.long.path')) {
  object.some.long.path = transform(oldValue);
}

As with the existing behaviour of declarations inside for, variables declared using let or const would be scoped to the individual if or while statement, rather than the containing block. In other words, the above syntax would be equivalent to the following currently-valid form I ended up writing:

{
  const oldValue = _.get(object, 'some.long.path');
  if (oldValue) object.some.long.path = transform(oldValue);
}

Another use case which C aficionados might recognise:

while (const c = getchar()) {
    process(c);
}

This syntax is already legal in C++, although not in C - in general this support is known as "anaphoric if", as it allows the body of the statement to refer back to the condition value. It's especially helpful in languages with truthiness, which ECMAScript has, as it allows access to the specific truthy value without further finagling.

Thoughts?

# Dan Peddle (8 years ago)

An alternative to the block which you ended up with is to extract that logic to a function, which provides something which can be tested too. Possibly overkill for oneliners like this though.

However, writing a lot of code like this myself (get a value, if it's truthy do something, else do other), that's a nice idea for an extension that doesn't add extra variable bindings to the containing scope.

# Viktor Kronvall (8 years ago)

Does this really need new semantic interpretation of the syntax? Using the Array.prototype methods .forEach and .map already mitigates this problem as far as I can tell by having a different bound variable (argument in this case) for each call.

I agree that the behavior may be non-intuitive if you have a background coming from Java or C++ but the implications would be quite far-reaching and the backward compatibility with previous versions would be difficult to handle. Wouldn't this require a new 'use strict'-like mode?

# Danielle McLean (8 years ago)

On 14 September 2016 at 17:58:24, Viktor Kronvall (viktor.kronvall at gmail.com(mailto:viktor.kronvall at gmail.com)) wrote:

Does this really need new semantic interpretation of the syntax? Using the Array.prototype methods .forEach and .map already mitigates this problem as far as I can tell by having a different bound variable (argument in this case) for each call.

I agree that the behavior may be non-intuitive if you have a background coming from Java or C++ but the implications would be quite far-reaching and the backward compatibility with previous versions would be difficult to handle. Wouldn't this require a new 'use strict'-like mode?

No, adding anaphoric if as I have described it will require neither new semantic interpretation of the syntax nor a new strictness directive. Currently, it is a syntax error to write a variable declaration within an if or while condition, so there is no valid code which contains the proposed syntax.

Also note that under this proposal, declarations made using the var keyword would still be hoisted to function scope, not scoped to the body associated with the condition - i.e., there would be no semantic difference whatsoever between the following two snippets:

if (var stuff = some.cool(expression)) doThings(stuff);
// equivalent to
var stuff;
if (stuff = some.cool(expression)) doThings(stuff);

Only declarations made with the newer let and const keywords, which are never hoisted to function scope anyway, would be narrowly scoped to the condition and its body.

if (let stuff = expr) doThings(stuff);
// equivalent to
{
  let stuff = expr;
  if (stuff) doThings(stuff);
}

(An aside: as the last example demonstrates, the if or while statement body should not need braces to isolate the scope in this way. This is consistent with the current behaviour for declarations in loops.)

# Jordan Harband (8 years ago)

While I like the idea of making it simpler to restrict the scope of variables, currently a reigning best practice is to never do assignment in conditionals, since it can be an easy typo from == or ===, and because it conflates assignment with expression truthiness, harming readability.

This seems like it runs afoul of the latter, certainly, and I haven't yet convinced myself whether it creates typo hazards (I'm thinking no, but wanted to bring it up just in case).

# Caitlin Potter (8 years ago)

On Sep 14, 2016, at 6:41 PM, Jordan Harband <ljharb at gmail.com> wrote:

While I like the idea of making it simpler to restrict the scope of variables, currently a reigning best practice is to never do assignment in conditionals, since it can be an easy typo from == or ===, and because it conflates assignment with expression truthiness, harming readability.

This seems like it runs afoul of the latter, certainly, and I haven't yet convinced myself whether it creates typo hazards (I'm thinking no, but wanted to bring it up just in case).

Given that let x == 10 or let x === 10 is illegal, it doesn’t seem too bad to allow if (let x = f(y)), LexicalDeclaration is visually distinct from EqualityExpression because of the let or const prefix.

# Oriol Bugzilla (8 years ago)

I like this proposal. Specially for while loops.

When I want to iterate all matches of a global regex, I always think the variable declaration at the top seems ugly

let match;
while (match = regex.exec(string)) {
  // ...
}

This looks better:

while (let match = regex.exec(string)) {
  // ...
}
# Jordan Harband (8 years ago)

For what it's worth, that specific use case would be addressed by tc39/String.prototype.matchAll ;-)

# Bergi (8 years ago)

Danielle McLean wrote:

variables declared using let or const would be scoped to the individual if or while statement, rather than the containing block. In other words, the above syntax would be equivalent to the following currently-valid form I ended up writing:

{
  const oldValue = _.get(object, 'some.long.path');
  if (oldValue) object.some.long.path = transform(oldValue);
}

What about else blocks, would the variables be available in them as well?

# Alan Johnson (8 years ago)

What about else if?

# Danielle McLean (8 years ago)

"Bergi" <a.d.bergi at web.de> wrote:

What about else blocks, would the variables be available in them as well?

No. If the else block executes, then the variable's value would be falsy and therefore presumably not useful for further processing. Of course, there are multiple falsy values in ES, but knowing which specific falsy value a condition produced is far less useful than knowing which specific truthy value a condition produced.

If you've got a use case for the variable's scope extending into the else block, though, feel free to change this. ;)

"Alan Johnson" <alan at breakrs.com> wrote:

What about else if?

As with a standalone else, the else if branches are only reached if the value from the if branch was falsy and therefore presumably not useful. So, again, the variable from the original if condition need not be available.

It should however be possible to declare another variable in an else if's condition, for consistency. Like this:

if (const x = expr()) {
  // use x
} else if (const x = expr2()) {
  // use x in a different way
} else {
  // couldn't get either kind of x :(
}
# Mark Volkmann (8 years ago)

For what it's worth, I'm on the side of people that do not want to see assignment statements in control structures. I don't think it is necessary and it results in code that is harder to read.

# Andy Earnshaw (8 years ago)

How so? Assignment is already possible in control structures. I think this reads better:

if (let a = foo) {

Than:

let a;

//...

if (a = foo) {

Having the let or const inside the conditional part clarifies the intent. There's an argument for not doing assignment inside the conditional part, but it's subjective and people will write code that way regardless. I'd much rather see let or const next to code like that if it means mistakes are less likely to be made.

# Kris Siegel (8 years ago)

I really like the idea of declaring a variable directly in the conditional itself. Many other languages do it. It's certainly not a critical thing (it makes 2 lines of code into 1) but I certainly would like it to happen. Considering it only moves the declaration I'm not convinced it makes code harder to read; in fact since you are explicitly saying it's part of the conditional you're declaring it in I feel like that makes it more clear as there is no longer a risk of someone re-using that variable outside of, say, the while loop it's declared in (which is what can happen today).

I know we don't have voting but this sure has my vote. I'd love to see it championed at an upcoming meeting.

# Andy Earnshaw (8 years ago)

The main barrier that I can see is that this requires new semantics. At the moment, variable declarations don't have any kind of usable result. If you consider

let a = eval('let b = 1');
console.log(a);
//-> undefined

This sounds like an easy thing to solve, but how about this:

if (let a = foo(), b = bar()) {
}

Does the block execute if a and b are both truthy, or just b? My gut instinct says "if bar() is truthy", but it still seems awkward. Any option might be confusing depending on your level of experience. What about this:

if (let { a, b } = foo) {
}

Does the block execute if a and b are both truthy, just b, or if foo is truthy? I'd lean towards foo in this case, but I think it looks pretty bad and I'd hate to see it in code. It might make more sense to throw for destructuring or if there's more than one assignment.

# Kris Siegel (8 years ago)

I agree but at the same time those examples simply look like bad coding practices to me and we can't completely hide from those. Since it's new semantics we shouldn't have to worry about it breaking something existing and it would be really easy to do a transpile back into ES5 or ES6 should people want to use it ahead of time.

Just my 2 cents. You certainly have valid points. The only awkward thing, in my opinion, is if we're using let and const we're going to have to allow var as well (it would be odd that, in a single place, you can use let and const but not var). Not sure how var should be really handled here; probably allow it but make it a bad practice to use var in this type of declaration. In my opinion anyway.

# Steve Fink (8 years ago)

On 09/12/2016 05:32 PM, Danielle McLean wrote:

In current ECMAScript, it is legal to place a variable declaration inside the initialiser of a for loop, as well as to declare the variable used by a for...in or for...of loop within the declaring expression:

 for (let i = 0; i < 5; ++i) console.log(i);
 for (let item of collection) process(item);

When this syntax is used with let or const, the resulting variable is scoped to the loop and is not visible to the rest of the surrounding block.

I propose that this syntax be extended, making it legal to place a variable declaration within the condition of an if or while statement. Any truthy value will cause the if block to run or while loop to repeat, as usual - the advantage is that the particular truthy value is bound to a variable and can be used inside the conditional block.

My initial reaction was positive, but now I don't think it works.

First, other places in the grammar do not restrict let/const to a single variable. Should

if (let a=0, b=1, c=0) { ... }

execute the if block or not? The obvious solution is to require a single variable, which means the grammar for these let/consts is different from others. What about

x = { a: 1 };
if (let {a} = x) { ... }

Second, that previous example makes it unclear to me at first glance what the intended semantics should be. I could imagine this printing either 1 or 2:

h = { foo: 0};
if (let {bar=1} = h) {
  print(1);
} else {
  print(2);
}

Is the conditional based on the variable's final value, or on whether or not the destructuring found a match? I could argue for either one, so even if there's a natural way to resolve my first problem, I think the code looks ambiguous to the eye.

if (let { children } = node) {
  print("interior node");
} else {
  print("leaf node");
}

Again, the simplest way to resolve this is to restrict it to "let/const IDENTIFIER = expression", but it feels weird to have different rules for this particular case. for(let...) on the other hand, does not attempt to use the let expression as a value, so it does not encounter any of these problems.

As a minor issue, it also feels a little awkward to special-case this conditional expression. I can do

if (let x = foo()) print(x)

but not

(let x = foo()) && print(x)
# J Decker (8 years ago)

Why not more generally - allow let/var declarations in expressions?

coming from a long and rich C background, I have no issues with the existing mechanisms... but for those languages that do support variable declarations in for loops; I've always wondered why not any expression?

if( let a = ( let b = 10 ) * 3 > 10 )

... or ...

c = (let a = b*d)

granted, the scope is extremely limited in the last case...

# Andrea Giammarchi (8 years ago)
if( let a = ( let b = 10 ) * 3 > 10 )

I've honestly no idea, at first/quick read, what the hell that would produce.

Is a going to be just true ? 'cause if not, this proposal violates operator precedence.

If yes ... why would anyone write that ?

# Jeremy Martin (8 years ago)

If yes ... why would anyone write that ?

I think it would have to be "yes" (and that's probably just a contrived example that doesn't demonstrate the usefulness).

Slightly less contrived, I could see the value in this, though. E.g.,

router.get('/user', (req, res, next) => {
    if (let user = req.session.user) {
        // do stuff with user here
    } else {
        res.status(401).end();
    }
});

I don't think it works as cleanly with var, but const and let has some nice precedence with for-statements.

# Isiah Meadows (8 years ago)

I say let's hold off until JavaScript gets pattern matching support (assuming it does). It's rather limiting otherwise, and the use case IMHO doesn't really merit a new syntax for it.

# Bob Myers (8 years ago)

I often find myself wanting to do this in the case of while. I've been writing

for (let a; a = someLongCondition();) { doSomethingWith(a); }

In that spirit, an oddball proposal:

while (let a = someLongCondition(); a) { ...use a... }
if (let a = someLongCondition(); a) { ...use a... }
# J Decker (8 years ago)

On Fri, Sep 16, 2016 at 2:43 AM, Bob Myers <rtm at gol.com> wrote:

I often find myself wanting to do this in the case of while. I've been writing

for (let a; a = someLongCondition();) { doSomethingWith(a); }

In that spirit, an oddball proposal:

while (let a = someLongCondition(); a) { ...use a... }
if (let a = someLongCondition(); a) { ...use a... }

switch( let a = something() ) { }

would ' do { ... use a ... } while( let a = f() ); ' work?

( (let a = f() ) > 3 ) ? (a-3) : (a+3) //ternary operator?

which begins to look like '( [let/var/const] ... ) ' in any expression.

# J Decker (8 years ago)

I saw it mentioned in several messages earlier about the comma operator as applied to the let/var statement... I suppose that IS an issue.

for example : if( let a = 1, b = 0 )

  1. if( 1, 0 ) the comma operator clears the expression stack and results with only the value after the comma... so you can chain some expressions that may have side effects... /* if( a=f(), b=g(a) ) */

  2. if the comma is then interpreted as an operator for the declaration, then the result is... undeterminate... because there's really not a testable result ?

# J Decker (8 years ago)

On Fri, Sep 16, 2016 at 10:43 AM, J Decker <d3ck0r at gmail.com> wrote:

I saw it mentioned in several messages earlier about the comma operator as applied to the let/var statement... I suppose that IS an issue.

for example : if( let a = 1, b = 0 )

  1. if( 1, 0 ) the comma operator clears the expression stack and results with only the value after the comma... so you can chain some expressions that may have side effects... /* if( a=f(), b=g(a) ) */

Sorry I did that again; ... that would really be like ' a = ( f(), b=g(a)) ' and would fail.... I always do that. maybe

if( ( a=f() ), g(a) ) intead?

# J Decker (8 years ago)

Why not more generally - allow let/var declarations in expressions?

coming from a long and rich C background, I have no issues with the existing mechanisms... but for those languages that do support variable declarations in for loops; I've always wondered why not any expression?

if( let a = ( let b = 10 ) * 3 > 10 ) ... or ...

c = (let a = b*d)

granted, the scope is extremely limited in the last case...

# Bergi (8 years ago)

Danielle McLean wrote:

variables declared using let or const would be scoped to the individual if or while statement, rather than the containing block. In other words, the above syntax would be equivalent to the following currently-valid form I ended up writing:

{
  const oldValue = _.get(object, 'some.long.path');
  if (oldValue) object.some.long.path = transform(oldValue);
}

What about else blocks, would the variables be available in them as well?

# Andy Earnshaw (8 years ago)

How so? Assignment is already possible in control structures. I think this reads better:

if (let a = foo) {

Than:

let a;

//...

if (a = foo) {

Having the let or const inside the conditional part clarifies the intent. There's an argument for not doing assignment inside the conditional part, but it's subjective and people will write code that way regardless. I'd much rather see let or const next to code like that if it means mistakes are less likely to be made.

# Alan Johnson (8 years ago)

What about else if?

# Dan Peddle (8 years ago)

An alternative to the block which you ended up with is to extract that logic to a function, which provides something which can be tested too. Possibly overkill for oneliners like this though.

However, writing a lot of code like this myself (get a value, if it's truthy do something, else do other), that's a nice idea for an extension that doesn't add extra variable bindings to the containing scope.

# Danielle McLean (8 years ago)

In current ECMAScript, it is legal to place a variable declaration inside the initialiser of a for loop, as well as to declare the variable used by a for...in or for...of loop within the declaring expression:

for (let i = 0; i < 5; ++i) console.log(i);     for (let item of collection) process(item);

When this syntax is used with let or const, the resulting variable is scoped to the loop and is not visible to the rest of the surrounding block.

I propose that this syntax be extended, making it legal to place a variable declaration within the condition of an if or while statement. Any truthy value will cause the if block to run or while loop to repeat, as usual - the advantage is that the particular truthy value is bound to a variable and can be used inside the conditional block. For example, here is the situation that prompted my writing this proposal:

if (const oldValue = _.get(object, 'some.long.path')) {       object.some.long.path = transform(oldValue);     }

As with the existing behaviour of declarations inside for, variables declared using let or const would be scoped to the individual if or while statement, rather than the containing block. In other words, the above syntax would be equivalent to the following currently-valid form I ended up writing:

{       const oldValue = _.get(object, 'some.long.path');       if (oldValue) object.some.long.path = transform(oldValue);     }

Another use case which C aficianados might recognise:

while (const c = getchar()) {         process(c);     }

This syntax is already legal in C++, although not in C - in general this support is known as "anaphoric if", as it allows the body of the statement to refer back to the condition value. It's especially helpful in languages with truthiness, which ECMAScript has, as it allows access to the specific truthy value without further finagling.

Thoughts?

# Isiah Meadows (8 years ago)

I say let's hold off until JavaScript gets pattern matching support (assuming it does). It's rather limiting otherwise, and the use case IMHO doesn't really merit a new syntax for it.

# Andy Earnshaw (8 years ago)

The main barrier that I can see is that this requires new semantics. At the moment, variable declarations don't have any kind of usable result. If you consider

let a = eval('let b = 1');
console.log(a);
//-> undefined

This sounds like an easy thing to solve, but how about this:

if (let a = foo(), b = bar()) {
}

Does the block execute if a and b are both truthy, or just b? My gut instinct says "if bar() is truthy", but it still seems awkward. Any option might be confusing depending on your level of experience. What about this:

if (let { a, b } = foo) {
}

Does the block execute if a and b are both truthy, just b, or if foo is truthy? I'd lean towards foo in this case, but I think it looks pretty bad and I'd hate to see it in code. It might make more sense to throw for destructuring or if there's more than one assignment.

# Jeremy Martin (8 years ago)

If yes ... why would anyone write that ?

I think it would have to be "yes" (and that's probably just a contrived example that doesn't demonstrate the usefulness).

Slightly less contrived, I could see the value in this, though. E.g.,

router.get('/user', (req, res, next) => {
    if (let user = req.session.user) {
        // do stuff with user here
    } else {
        res.status(401).end();
    }
});

I don't think it works as cleanly with var, but const and let has some nice precedence with for-statements.

# Mark Volkmann (8 years ago)

For what it's worth, I'm on the side of people that do not want to see assignment statements in control structures. I don't think it is necessary and it results in code that is harder to read.

# Viktor Kronvall (8 years ago)

Does this really need new semantic interpretation of the syntax? Using the Array.prototype methods .forEach and .map already mitigates this problem as far as I can tell by having a different bound variable (argument in this case) for each call.

I agree that the behavior may be non-intuitive if you have a background coming from Java or C++ but the implications would be quite far-reaching and the backward compatibility with previous versions would be difficult to handle. Wouldn't this require a new 'use strict'-like mode? 2016年9月14日(水) 7:51 Dan Peddle <dan at flarework.com>:

# Danielle McLean (8 years ago)

"Bergi" <a.d.bergi at web.de> wrote:

What about else blocks, would the variables be available in them as well?

No. If the else block executes, then the variable's value would be falsy and therefore presumably not useful for further processing. Of course, there are multiple falsy values in ES, but knowing which specific falsy value a condition produced is far less useful than knowing which specific truthy value a condition produced.

If you've got a use case for the variable's scope extending into the else block, though, feel free to change this. ;)

"Alan Johnson" <alan at breakrs.com> wrote:

What about else if?

As with a standalone else, the else if branches are only reached if the value from the if branch was falsy and therefore presumably not useful. So, again, the variable from the original if condition need not be available.

It should however be possible to declare another variable in an else if's condition, for consistency. Like this:

if (const x = expr()) {
  // use x
} else if (const x = expr2()) {
  // use x in a different way
} else {
  // couldn't get either kind of x :(
}
# Kris Siegel (8 years ago)

I agree but at the same time those examples simply look like bad coding practices to me and we can't completely hide from those. Since it's new semantics we shouldn't have to worry about it breaking something existing and it would be really easy to do a transpile back into ES5 or ES6 should people want to use it ahead of time.

Just my 2 cents. You certainly have valid points. The only awkward thing, in my opinion, is if we're using let and const we're going to have to allow var as well (it would be odd that, in a single place, you can use let and const but not var). Not sure how var should be really handled here; probably allow it but make it a bad practice to use var in this type of declaration. In my opinion anyway.

# Danielle McLean (8 years ago)

In current ECMAScript, it is legal to place a variable declaration inside the initialiser of a for loop, as well as to declare the variable used by a for...in or for...of loop within the declaring expression:

for (let i = 0; i < 5; ++i) console.log(i);     for (let item of collection) process(item);

When this syntax is used with let or const, the resulting variable is scoped to the loop and is not visible to the rest of the surrounding block.

I propose that this syntax be extended, making it legal to place a variable declaration within the condition of an if or while statement. Any truthy value will cause the if block to run or while loop to repeat, as usual - the advantage is that the particular truthy value is bound to a variable and can be used inside the conditional block. For example, here is the situation that prompted my writing this proposal:

if (const oldValue = _.get(object, 'some.long.path')) {       object.some.long.path = transform(oldValue);     }

As with the existing behaviour of declarations inside for, variables declared using let or const would be scoped to the individual if or while statement, rather than the containing block. In other words, the above syntax would be equivalent to the following currently-valid form I ended up writing:

{       const oldValue = _.get(object, 'some.long.path');       if (oldValue) object.some.long.path = transform(oldValue);     }

Another use case which C aficianados might recognise:

while (const c = getchar()) {         process(c);     }

This syntax is already legal in C++, although not in C - in general this support is known as "anaphoric if", as it allows the body of the statement to refer back to the condition value. It's especially helpful in languages with truthiness, which ECMAScript has, as it allows access to the specific truthy value without further finagling.

Thoughts?

# Kris Siegel (8 years ago)

I really like the idea of declaring a variable directly in the conditional itself. Many other languages do it. It's certainly not a critical thing (it makes 2 lines of code into 1) but I certainly would like it to happen. Considering it only moves the declaration I'm not convinced it makes code harder to read; in fact since you are explicitly saying it's part of the conditional you're declaring it in I feel like that makes it more clear as there is no longer a risk of someone re-using that variable outside of, say, the while loop it's declared in (which is what can happen today).

I know we don't have voting but this sure has my vote. I'd love to see it championed at an upcoming meeting.

# Danielle McLean (8 years ago)

On 14 September 2016 at 17:58:24, Viktor Kronvall (viktor.kronvall at gmail.com(mailto:viktor.kronvall at gmail.com)) wrote:

Does this really need new semantic interpretation of the syntax? Using the Array.prototype methods .forEach and .map already mitigates this problem as far as I can tell by having a different bound variable (argument in this case) for each call.

I agree that the behavior may be non-intuitive if you have a background coming from Java or C++ but the implications would be quite far-reaching and the backward compatibility with previous versions would be difficult to handle. Wouldn't this require a new 'use strict'-like mode?

No, adding anaphoric if as I have described it will require neither new semantic interpretation of the syntax nor a new strictness directive. Currently, it is a syntax error to write a variable declaration within an if or while condition, so there is no valid code which contains the proposed syntax.

Also note that under this proposal, declarations made using the var keyword would still be hoisted to function scope, not scoped to the body associated with the condition - i.e., there would be no semantic difference whatsoever between the following two snippets:

if (var stuff = some.cool(expression)) doThings(stuff);     // equivalent to     var stuff;     if (stuff = some.cool(expression)) doThings(stuff);

Only declarations made with the newer let and const keywords, which are never hoisted to function scope anyway, would be narrowly scoped to the condition and its body.

if (let stuff = expr) doThings(stuff);     // equivalent to     {       let stuff = expr;       if (stuff) doThings(stuff);     }

(An aside: as the last example demonstrates, the if or while statement body should not need braces to isolate the scope in this way. This is consistent with the current behaviour for declarations in loops.)

# Caitlin Potter (8 years ago)

Given that let x == 10 or let x === 10 is illegal, it doesn’t seem too bad to allow if (let x = f(y)), LexicalDeclaration is visually distinct from EqualityExpression because of the let or const prefix.

On Sep 14, 2016, at 6:41 PM, Jordan Harband <ljharb at gmail.com> wrote:

While I like the idea of making it simpler to restrict the scope of variables, currently a reigning best practice is to never do assignment in conditionals, since it can be an easy typo from == or ===, and because it conflates assignment with expression truthiness, harming readability.

This seems like it runs afoul of the latter, certainly, and I haven't yet convinced myself whether it creates typo hazards (I'm thinking no, but wanted to bring it up just in case).

Given that let x == 10 or let x === 10 is illegal, it doesn’t seem too bad to allow if (let x = f(y)), LexicalDeclaration is visually distinct from EqualityExpression because of the let or const prefix.

# Steve Fink (8 years ago)

On 09/12/2016 05:32 PM, Danielle McLean wrote:

In current ECMAScript, it is legal to place a variable declaration inside the initialiser of a for loop, as well as to declare the variable used by a for...in or for...of loop within the declaring expression:

 for (let i = 0; i < 5; ++i) console.log(i);
 for (let item of collection) process(item);

When this syntax is used with let or const, the resulting variable is scoped to the loop and is not visible to the rest of the surrounding block.

I propose that this syntax be extended, making it legal to place a variable declaration within the condition of an if or while statement. Any truthy value will cause the if block to run or while loop to repeat, as usual - the advantage is that the particular truthy value is bound to a variable and can be used inside the conditional block.

My initial reaction was positive, but now I don't think it works.

First, other places in the grammar do not restrict let/const to a single variable. Should

if (let a=0, b=1, c=0) { ... }

execute the if block or not? The obvious solution is to require a single variable, which means the grammar for these let/consts is different from others. What about

x = { a: 1 }; if (let {a} = x) { ... }

Second, that previous example makes it unclear to me at first glance what the intended semantics should be. I could imagine this printing either 1 or 2:

h = { foo: 0}; if (let {bar=1} = h) { print(1); } else { print(2); }

Is the conditional based on the variable's final value, or on whether or not the destructuring found a match? I could argue for either one, so even if there's a natural way to resolve my first problem, I think the code looks ambiguous to the eye.

if (let { children } = node) { print("interior node"); } else { print("leaf node"); }

Again, the simplest way to resolve this is to restrict it to "let/const IDENTIFIER = expression", but it feels weird to have different rules for this particular case. for(let...) on the other hand, does not attempt to use the let expression as a value, so it does not encounter any of these problems.

As a minor issue, it also feels a little awkward to special-case this conditional expression. I can do

if (let x = foo()) print(x)

but not

(let x = foo()) && print(x)

# Jordan Harband (8 years ago)

For what it's worth, that specific use case would be addressed by tc39/String.prototype.matchAll ;-)

# Andrea Giammarchi (8 years ago)

if( let a = ( let b = 10 ) * 3 > 10 )

I've honestly no idea, at first/quick read, what the hell that would produce.

Is a going to be just true ? 'cause if not, this proposal violates operator precedence.

If yes ... why would anyone write that ?

# Oriol Bugzilla (8 years ago)

I like this proposal. Specially for while loops.

When I want to iterate all matches of a global regex, I always think the variable declaration at the top seems ugly

let match;
while (match = regex.exec(string)) {
  // ...
}

This looks better:

while (let match = regex.exec(string)) {
  // ...
}
# Jordan Harband (8 years ago)

While I like the idea of making it simpler to restrict the scope of variables, currently a reigning best practice is to never do assignment in conditionals, since it can be an easy typo from == or ===, and because it conflates assignment with expression truthiness, harming readability.

This seems like it runs afoul of the latter, certainly, and I haven't yet convinced myself whether it creates typo hazards (I'm thinking no, but wanted to bring it up just in case).

# J Decker (8 years ago)

I saw it mentioned in several messages earlier about the comma operator as applied to the let/var statement... I suppose that IS an issue.

for example : if( let a = 1, b = 0 )

  1. if( 1, 0 ) the comma operator clears the expression stack and results with only the value after the comma... so you can chain some expressions that may have side effects... /* if( a=f(), b=g(a) ) */

  2. if the comma is then interpreted as an operator for the declaration, then the result is... undeterminate... because there's really not a testable result ?

# Bob Myers (8 years ago)

I often find myself wanting to do this in the case of while. I've been writing

for (let a; a = someLongCondition();) { doSomethingWith(a); }

In that spirit, an oddball proposal:

while (let a = someLongCondition(); a) { ...use a... }
if (let a = someLongCondition(); a) { ...use a... }
# J Decker (8 years ago)

On Fri, Sep 16, 2016 at 10:43 AM, J Decker <d3ck0r at gmail.com> wrote:

I saw it mentioned in several messages earlier about the comma operator as applied to the let/var statement... I suppose that IS an issue.

for example : if( let a = 1, b = 0 )

  1. if( 1, 0 ) the comma operator clears the expression stack and results with only the value after the comma... so you can chain some expressions that may have side effects... /* if( a=f(), b=g(a) ) */

Sorry I did that again; ... that would really be like ' a = ( f(), b=g(a)) ' and would fail.... I always do that. maybe

if( ( a=f() ), g(a) ) intead?

# J Decker (8 years ago)

On Fri, Sep 16, 2016 at 2:43 AM, Bob Myers <rtm at gol.com> wrote:

I often find myself wanting to do this in the case of while. I've been writing

for (let a; a = someLongCondition();) { doSomethingWith(a); }

In that spirit, an oddball proposal:

while (let a = someLongCondition(); a) { ...use a... }
if (let a = someLongCondition(); a) { ...use a... }

switch( let a = something() ) { }

would ' do { ... use a ... } while( let a = f() ); ' work?

( (let a = f() ) > 3 ) ? (a-3) : (a+3) //ternary operator?

which begins to look like '( [let/var/const] ... ) ' in any expression.