It’s currently hard to create compound expressions that do anything non-trivial. And it would be useful to be able to bind local variables and execute statements in an unobtrusive way, without introducing refactoring hazards like capturing the meaning of return.
We had less success with lambdas, but let is a less powerful and more orthogonal construct.
Expression ::= ... | "let" LetHead "{" Statement* ("=>" Expression)? "}"
LetHead ::= "(" LetBindingList ")"
LetBindingList ::= LetBinding | LetBindingList "," LetBinding
LetBinding ::= Pattern ("=" AssignmentExpression)?
| Ident ("=" AssignmentExpression)?
See destructuring for the meaning of Pattern.
undefined valueundefined valuelet vs let*
In typical programming, let* tends to be more convenient; combats rightward-drift, and it’s rare that you need something not to be in scope in ordinary programming.
But for code generation, including desugaring spec’s, future-proofing for macros, and supporting JS-as-x86, it’s a little harder to achieve let from let* than vice versa. Probably possible e.g. with a destructuring LHS:
let ({ x, y, z } = { x: foo(), y: bar(), z: baz() }) { ... }
This won’t get you any duplicate-variable checking, but it will ensure that none of the LHS’s are in scope on the RHS’s. Anyway it’s uncommon for ES to enforce no-dups. Even function args can be dups.
LetHead optional – probably impossible
Ambiguity with destructuring let declaration:
let { x }; // variable expression statement or property-destructuring? let { x: x }; // labelled variable expression statement or property-destructuring?
With the completion value, statements are almost expressions anyway; why not introduce a syntax for making the completion more statically predictable? Then this spec just becomes:
Block ::= "{" Statement* ("=>" Expression)? "}"
Expression ::= ... | "let" LetHead Block
The result value of a let expression with no tail expression could be the completion value instead of undefined.
let (x = ...) { statement() >= x }
— Waldemar Horwat 2010/05/22 00:49