rfcs/docs/syntax-destructuring.md
2025-01-25 14:04:36 -08:00

134 lines
No EOL
3.8 KiB
Markdown

# Destructuring
## Summary
Introduce a set of destructuring utilities that:
- work with array & dictionary style tables
- work with both declaration and assignment
- don't require parser backtrack
- are consistent with Luau style
```Lua
-- TODO
```
## Motivation
This is intended as a spiritual successor to the older ["Key destructuring" RFC by Kampfkarren](https://github.com/luau-lang/rfcs/pull/24), which was very popular but was unfortunately not able to survive implementation concerns.
----
Simple indexes on tables are very common both in and outside of Luau. A common use case is large libraries. It is common in the web world to see something like:
```js
const { useState, useEffect } = require("react");
```
...which allows you to quickly use `useState` and `useEffect` without fully qualifying it in the form of `React.useState` and `React.useEffect`. In Luau, if you do not want to fully qualify common React functions, the top of your file will often look like:
```lua
local useEffect = React.useEffect
local useMemo = React.useMemo
local useState = React.useState
-- etc
```
...which creates a lot of redundant cruft.
It is also common to want to have short identifiers to React properties, which basically always map onto a variable of the same name. As an anecdote, a regex search of `^\s+local (\w+) = \w+\.\1$` comes up 103 times in the My Movie codebase, many in the form of indexing React properties:
```lua
local position = props.position
local style = props.style
-- etc...
```
...whereas in JavaScript this would look like:
```js
const { position, style } = props
// Supported in JavaScript, but not this proposal
function MyComponent({
position,
style,
})
```
React properties are themselves an example of a common idiom of passing around large tables as function arguments, such as with HTTP requests:
```js
// JavaScript
get("/users", ({
users,
nextPageCursor,
}) => { /* code */ })
```
## Design
### Multiple indexing
The RFC is built around the idea of being able to index multiple keys in a table simultaneously. The simplest form of this idea is introduced.
The `[]` indexing operator is extended to support comma-separated arguments, to simultaneously read from multiple keys at once:
```
local numbers = {3, 5, 11}
local three, five, eleven = numbers[1, 2, 3]
```
### Range indexing
Multiple indexing can replace manual unpacking of tables with `table.unpack`.
Instead of providing a list of keys, an inclusive range of keys can be specified with `[x : y]`, where `x` and `y` evaluate to a number.
```
local numbers = {3, 5, 11}
local three, five, eleven = numbers[1 : 3]
```
Negative numbers are allowed, with symmetric behaviour to other Luau functions that accept negative indices (subtracting from the length of the table).
```
local numbers = {3, 5, 11}
local three, five, eleven = numbers[1 : -1]
```
This can be extended to other types such as strings and buffers, to replace the relevant operations in their libraries.
```
local text = "Hello, world"
local where = text[8 : -1]
```
## Alternatives
The previously popular RFC used braces around the list of identifiers to signal destructuring, and dot prefixes to disambiguate array and dictionary destructuring:
```Lua
local rootUtils = require("../rootUtils")
local { .homeDir, .workingDir } = rootUtils.rootFolders
```
One reservation cited would be that this is difficult to implement for assignments without significant backtracking:
```Lua
local rootUtils = require("../rootUtils")
{ .homeDir, .workingDir } = rootUtils.rootFolders
```
Removing the braces and relying on dot prefixes is not a solution, as this still requires significant backtracking to resolve:
```Lua
local rootUtils = require("../rootUtils")
.homeDir, .workingDir = rootUtils.rootFolders
```
As such, this proposal does not pursue these design directions further.