revive let blocks

# Kyle Simpson (9 years ago)

I'd like to ask if there's anyone on TC39 that would be willing to champion a proposal to add the let-block (let-statement) syntax?

I currently write my block-scoped declarations as:

{ let a = 2, b, c;
	// ..
}

I do this because I want to be in the habit of always putting my let declarations at the top of blocks to avoid TDZ hazards. However, Firefox has long had the alternate let-block/statement syntax, which I prefer:

let (a = 2, b, c) {
	// ..
}

Would there be support to consider such a proposal?

Side note: I'd also be in favor of a const (a = 2) { .. } form, if the symmetry was appealing.

# Benjamin Gruenbaum (9 years ago)

Apart from complicating the engine and the grammar - what advantage does the second version have over the first one? Why do you prefer it to the first one? (Genuinely asking)

I'm also not aware of any other languages that provide this (although that's not a huge issue).

# Bucaran (9 years ago)

Another way: Instead of using let at all, why not creating a function and pass it your a, b and c as arguments.

Nowadays I try to program without explicitly declaring any variables, hence my suggestion.

(function (a, b, c) {
    
}(2))

If you can post a more concrete example I would give you a more concrete suggestion to what I mean.

Cheers

# Kyle Simpson (9 years ago)

Apart from complicating the engine and the grammar

I've tried to figure out what complications it introduces. In my imperfect analysis, it hasn't seemed like much. I've written a transpiler tool[1] that finds let (x..) { .. } occurrences and changes them to { let x.. .. }. It was pretty easy to do. I would imagine a similar technique could work for other transpilers and even the engines themselves (simple AST transformation).

I'm sure there are other/obscure issues I'm missing, but the fact that this still works in FF leads me to believe it is a tenable feature at least.

what advantage does the second version have over the first one?

The primary advantage is that it's an explicit form that syntactically forces the main let declarations to the top of the block, eliminating the TDZ hazard (at least for those).

Ofc that's not to say that you can't also do other let declarations inside the block and shoot yourself in the foot. But at least for the main ones you're declaring for the block, it's clear and obvious what variables will exist for that block's scope by looking at the top of the block.

That notion is similar to the advantages many devs feel/felt from putting all the var declarations at the top of a function declaration, or locating the formal function parameters explicitly in the function declaration instead of implicitly pulling in local variable declarations from arguments:

function foo() {
   var [a,b,c] = arguments;
   // ..
}

// vs:

function bar(a,b,c) {
   // ..
}

Why do you prefer it to the first one?

I prefer creating explicit blocks for scope rather than implicitly hijacking existing blocks for scope. I prefer to be able to reason about my if block separately from a localized block of scope that may appear inside it. That's why I create the explicit { .. } block to put my let declarations in. And that's why the next logical step is to move the let declarations to a syntactic form that forcibly attaches them to the block, making the purpose/intent of the block all that much clearer.

[1] getify/let

# Kyle Simpson (9 years ago)

(function (a, b, c) {

}(2))

The main disadvantage of that style over the one I'm advocating for is that it visually separates the variable declaration (a) from its value initialization (2). If there's 5, 10, or more lines of code in between them, it makes it much harder to figure out the initial state of variables as they enter a "block".

Also, obviously, a function call is more heavy weight (performance wise) than a block with scoped declarations.

# Herby Vojčík (9 years ago)

Benjamin Gruenbaum wrote:

Apart from complicating the engine and the grammar - what advantage does the second version have over the first one? Why do you prefer it to the first one? (Genuinely asking)

I'm also not aware of any other languages that provide this (although that's not a huge issue).

IIRC, Standard ML of New Jersey had a keyword 'local' for this (but it was long ago when I has that course in university).

# Gary Guo (9 years ago)

Be aware that the way you utilize 'let' will be a breaking change. In ES5 and ES6let(a=1) {}is valid and it is a call to let with one argument followed by a block. Your suggestions will turn it into a let-block. In ES6 let is considered as an identifier and the meaning is created by special syntactic grammar. Personal idea: I consider this is unnecessary. If you consider use of block without context is ugly, use if(true) or do-while(false)

# Bucaran (9 years ago)

I see what you mean.

I always try to favor functional-style JavaScript (creating a big expression composed of several functions, both as arguments or declared somewhere, using recursion instead of keeping state, not using control structures, no variables, etc), hence my suggestion.

Performance wise you are (very probably) right, but I am willing to pay the tradeoff and live the future now, or whenever I can :)

Another idea I find fun it’s binding this to an object with my data:

function f () {
}

f.call({a:2, b, c})
# Kyle Simpson (9 years ago)

Be aware that the way you utilize 'let' will be a breaking change. In ES5 and ES6

In addition to the fact that this feature is long since co-existing in FF and doesn't seem to have broken the web, IIUC, there was already a "breaking change" in ES6, with let and destructuring:

let[x] = foo();

I believe it was deemed that the chances of that breakage being widespread were low enough to warrant the let feature anyway. I would postulate (though I don't have the facility to test it) that the exact function-call-and-block pattern let(x) { .. } would be as much or less likely to occur in legacy code than let[x] = foo().

If you consider use of block without context is ugly, use if(true) or do-while(false)

Those options are much uglier than the standalone { .. } block.

# Benjamin Gruenbaum (9 years ago)

From: Kyle Simpson <getify at gmail.com>

To: "es-discuss at mozilla.org" <es-discuss at mozilla.org>

Cc: Date: Thu, 18 Jun 2015 07:34:28 -0500 Subject: Re: revive let blocks

(function (a, b, c) {

}(2))

The main disadvantage of that style over the one I'm advocating for is

that it visually separates the variable declaration (a) from its value initialization (2).

If that's the problem you can do

((a = 2, b, c) => {

})();

And engines can optimize - but I can see the merit of explicit syntax for this.

I prefer creating explicit blocks for scope rather than implicitly

hijacking existing blocks for scope. I prefer to be able to reason about my if block separately from a localized block of scope that may appear inside it. That's why I create the explicit { .. } block to put my let declarations in. And that's why the next logical step is to move the let declarations to a syntactic form that forcibly attaches them to the block, making the purpose/intent of the block all that much clearer.

Well, traditionally this is something we've been moving from in languages

  • for example in C (before C99) you used to have to declare variables on the top of a scope but don't anymore. In Pascal, you had var for this and this was complained about by people.

Why would a syntactic addition to the language be better than a linter here that verifies where you declared your variables? The only issue I see here where this sort of thing can be really important (and justify adding it for other cases anyway) is stack allocation in a dynamic language - but I'm not sure how that proposal looks like yet and from what I understand it'll be discussed in the July TC meeting.

If anyone could shed light on the current ideas for stack allocation that'd be great :)

# Herby Vojčík (9 years ago)

Benjamin Gruenbaum wrote:

From: Kyle Simpson <getify at gmail.com <mailto:getify at gmail.com>> To: "es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>" <es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>> Cc: Date: Thu, 18 Jun 2015 07:34:28 -0500 Subject: Re: revive let blocks

(function (a, b, c) {

}(2))

The main disadvantage of that style over the one I'm advocating for is that it visually separates the variable declaration (a) from its value initialization (2).

If that's the problem you can do

((a = 2, b, c) => {

})();

Well, I personally think this example is good enough and engines surely optimize already (IIFE is here long enough so that it is optimized).

# Boris Zbarsky (9 years ago)

On 6/18/15 9:01 AM, Kyle Simpson wrote:

In addition to the fact that this feature is long since co-existing in FF and doesn't seem to have broken the web

Firefox doesn't ship let support on the web by default yet. For example, this HTML:

<script>let x = 5;</script>

will result in:

SyntaxError: missing ; before statement

at the location of the 'x'.

You have to explicitly opt in to let support in Firefox right now via language="javascript1.8" or similar.

That's not to say this change would break the web; just that we have no evidence it doesn't.

# Bradley Meck (9 years ago)

(a=1)=>{ console.log(a) }()

Is less verbose than an IIFE and keeps this the same. Also keeps the initialization and init of the variable in the same place.

# Axel Rauschmayer (9 years ago)

On 18 Jun 2015, at 15:59 , Bradley Meck <bradley.meck at gmail.com> wrote:

(a=1)=>{ console.log(a) }()

Is less verbose than an IIFE and keeps this the same. Also keeps the initialization and init of the variable in the same place.

Neat trick. Caveat – you need parentheses around the arrow function:

((a=1)=>{
  console.log(a)
})();
# Rick Waldron (9 years ago)

On Thu, Jun 18, 2015 at 9:54 AM Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 6/18/15 9:01 AM, Kyle Simpson wrote:

In addition to the fact that this feature is long since co-existing in FF and doesn't seem to have broken the web

Firefox doesn't ship let support on the web by default yet. For example, this HTML:

<script>let x = 5;</script>

will result in:

SyntaxError: missing ; before statement

at the location of the 'x'.

Strange, this works in the console, but not in a script i.gyazo.com/e55d26495c3fe8b01938fe1b99664682.png

You have to explicitly opt in to let support in Firefox right now via language="javascript1.8" or similar.

More importantly, the only reason let (names) {} "co-exists" in those special opt-in versions is because let is not allowed as an identifier. The issue that Gary Guo pointed out is a very real breaking change for the web.

Using language="javascript1.8" will make all of these result in SyntaxError:

var let = 1; var let = function() {}; function let() {} let(1);

Otherwise this would be ambiguous:

let (a) {}

Adding this non-standard restriction prevented authors from using let as an identifier.

I wonder if some application of [no LineTerminator here] might make this work? Is it worth it?

# Boris Zbarsky (9 years ago)

On 6/18/15 11:30 AM, Rick Waldron wrote:

Strange, this works in the console, but not in a script i.gyazo.com/e55d26495c3fe8b01938fe1b99664682.png

Yep, it's entirely possible the console opts in to let.

It also exposes some APIs that are not exposed in web pages by default and a few other interesting things like that... Evaluating things in the console and in the currently loaded web page are almost the same, but not quite.

In addition, in the particular screenshot you have there the currently loaded page is the Firefox start page, which is not exactly a normal web page either, so may have its own interesting things going on with both the script environment and its global.

# Rick Waldron (9 years ago)

On Thu, Jun 18, 2015 at 12:55 PM Boris Zbarsky <bzbarsky at mit.edu> wrote:

On 6/18/15 11:30 AM, Rick Waldron wrote:

Strange, this works in the console, but not in a script i.gyazo.com/e55d26495c3fe8b01938fe1b99664682.png

Yep, it's entirely possible the console opts in to let.

It also exposes some APIs that are not exposed in web pages by default and a few other interesting things like that... Evaluating things in the console and in the currently loaded web page are almost the same, but not quite.

In addition, in the particular screenshot you have there the currently loaded page is the Firefox start page, which is not exactly a normal web page either, so may have its own interesting things going on with both the script environment and its global.

That's interesting, thanks for the follow up!

# Kyle Simpson (9 years ago)

Just to wrap this thread up, quoting myself from another thread:

"In any case, I won't push my proposal anymore."


But for posterity sake, wanted to make one last comment as to why the various suggestions for IIFE's and arrow expressions are inappropriate for the task: they change (hijack) the behavior of return, break, and continue. A standalone block like { let x = 2; .. } or let (x = 2) { .. } can be placed anywhere, inside a function, loop, etc, and not hijack these types of statements.

I'll be sticking with:

{ let x = 42;

    console.log("The meaning of JS: ", x);

}

Appreciate the various thoughtful responses.

# Bucaran (9 years ago)

Hey Kyle

True for continue and break, but maybe it’s about time we stop using these archaic control structures anyway :)

As for return I don’t see what’s the problem if you return your value inside the IIFE as well.

# Herby Vojčík (9 years ago)

Hello!

Though this is a kind of syntax is probably macroable, interesting idea appeared in my mind regarding let blocks, so I would show it here, maybe it can actually be interesting for others as well.

The idea is to use existing let statement and extending it so one can include { ... code ... } block in place of last assignment. Hold on for a while: this form can merge with do-expressions by using 'let', not 'do' as the keyword:

let a = 4, b = 3; // normal let let { throw new Error("Throw in an expression"); } // let-expression let a = 4, b = 3, { a + b } // let-expression with own local lets

The third form is more or less the let-block from the PoV of reader, even if in fact is a new do-expression using let keyword with some let-assigment local to that block happening before.

I see a 'problem' that I can only distinguish if it is a let-statement or let-expression at the end of it, but afaict it does not pose any real gotchas for the compiler - it accumulates the assignment and at the either make them let-statement and use them for the rest of enclosing block or makes it let-expression and use them only locally.

# Matthew Robb (9 years ago)

I feel like this would just get confused with object destructuring.

# Brendan Eich (9 years ago)

Matthew Robb wrote:

I feel like this would just get confused with object destructuring.

Not just confused -- it is fatally ambiguous.

I think do expressions are "go" for ES7 but they need re-championing, and implementation. I'll stir the pot.

# Matthew Robb (9 years ago)

On Thu, Jun 25, 2015 at 6:20 PM, Brendan Eich <brendan at mozilla.org> wrote:

I think do expressions are "go" for ES7 but they need re-championing, and implementation. I'll stir the pot.

​Might this include something like the following form?: do(let x = 1) {}​. Seems like that might satisfy Kyle's original request.

  • Matthew Robb
# Kevin Smith (9 years ago)

I think do expressions are "go" for ES7 but they need re-championing, and implementation. I'll stir the pot.

I think Andreas was interested at some point?

I've long been interested in the possibility of a "do async" as well. : )

# Andreas Rossberg (9 years ago)

On 26 June 2015 at 04:08, Kevin Smith <zenparsing at gmail.com> wrote:

I think do expressions are "go" for ES7 but they need re-championing,

and implementation. I'll stir the pot.

I think Andreas was interested at some point?

Yes, I'm planning to prepare a proper spec for the September meeting. I'm also hoping we get a prototype impl in V8 some time later this year, although I'd rather not promise anything.

# Kevin Smith (9 years ago)

Yes, I'm planning to prepare a proper spec for the September meeting. I'm also hoping we get a prototype impl in V8 some time later this year, although I'd rather not promise anything.

Cool - looking forward to it!