parser
parser copied to clipboard
cell destructuring
Destructuring allows a cell to expose multiple values.
({foo, bar}) = ({foo: 1, bar: 2})
({foo, bar}) = { return {foo: 1, bar: 2}; }
([foo, ...bar]) = [1, 2, 3]
This PR allows the cell.id to be an ObjectPattern or an ArrayPattern. There’s still a fair amount of work on the compiler side to turn this into the corresponding set of variables, but this serves as the language proposal.
TODO
- [x] tests
- [x] support default values in array patterns
- [x] support default values in object patterns
- [x] capture references to other cells in default value expressions
- [x] capture features from default value expressions
- [x] don’t allow await or yield inside default value expressions
- [x] don’t allow destructuring into comma expressions
This initial implementation looks good, and the functionality it provides is highly desirable.
While there are going to be a few small considerations to keep in mind in the UI when the adopt destructuring (permalink names for cells, display of multiple names in the cell menu, embed code generation for a cell that exposes multiple values...), those are all fairly easily surmountable.
I'd still like to register my (already verbally discussed) mild objection to this syntax. We’re appropriating the parenthetical declaration of destructured function arguments, instead of the JavaScript canonical parenthetical wrapping of the entire destructuring assignment. I'd prefer that we hew to JavaScript if we’re going to add this.
For example, JavaScript:
let a, b;
...
({a, b} = expression);
But in this proposal:
({a, b}) = expression
I'd prefer:
({a, b} = expression)
... and of course, dropping the parentheses altogether would be best.
There’s quite a bit more discussion about the ES6 parse choices of ({a, b}) = ...
vs ({a, b} = ...)
in this GitHub thread, as well as the special note in the ES spec forbidding expression statements from starting with {
.
Thanks for the references and feedback!
It feels unfair (to me) to say that one of these approaches “hews to JavaScript” while the other doesn’t. Named cells are not assignment expressions; they declare a binding between one or more identifiers and a function body, the latter being invoked automatically by the runtime. In my view cells are closest to arrow functions.
({foo, bar}) => body
The difference is that the name of the cell binds to the output rather than the input. Hence, my proposal to put the parens around the binding.
({foo, bar}) = body
The other (lexical binding) precedent to consider is a variable declaration.
let {foo, bar} = expression;
We could use a keyword to make it immediately clear whether a cell is named and avoid ambiguity. That could be a new keyword (such as cell
) or the exist const keyword.
const {foo, bar} = body
Simple names could be written this way, too, if desired.
const foo = body
And you could possibly have a const mutable, which is a little weird.
const mutable foo = body
The reason I’ve avoided a keyword (from the beginning) is that it feels like a lot of noise for very little signal. Cells are already delineated as separate scripts, and in the common case only a couple tokens are needed to determine whether a cell is named. Introducing a new keyword to support destructuring feels more disruptive than necessary.
I realized that both of these proposals
(foo) = body
(foo = body)
are potentially not backwards-compatible with mutable assignments, though my guess there are few (if any) cells that would break. In the parens-around-binding proposal, a mutable assignment could change to a mutable declaration.
(mutable foo) = body
In the parens-around-cell proposal, a mutable assignment could change to a mutable declaration, too.
(mutable foo = body)
Also, in the parens-around-binding proposal, previously invalid syntax (assignment to constant variable foo) could become a valid cell declaration.
(foo) = 42
This is true of the parens-around-cell proposal, too.
(foo = 42)
Personally, I find the parens-around-cell proposal looks more like an assignment expression and less like a lexical binding, and hence I prefer the parens around the binding. :smile:
Any chance that this can be merged? Just going through the feedback repo and I think this would close https://github.com/observablehq/feedback/issues/48
Honestly can't wait for this feature to be released.