From 64cca6e158104557a4d1de19b11ce9fcc9a0bbd4 Mon Sep 17 00:00:00 2001 From: crywink <30543384+crywink@users.noreply.github.com> Date: Fri, 5 Aug 2022 22:49:39 -0500 Subject: [PATCH] Create table-destructuring.md --- rfcs/table-destructuring.md | 236 ++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 rfcs/table-destructuring.md diff --git a/rfcs/table-destructuring.md b/rfcs/table-destructuring.md new file mode 100644 index 00000000..ded7eb53 --- /dev/null +++ b/rfcs/table-destructuring.md @@ -0,0 +1,236 @@ +# Table Destructuring + +## Summary +A feature present and highly-used in JavaScript, table destructuring allows the developer to extract data from tables and assign them to variables in a clean, shortened manner, thus avoiding the need for excessive index calls and several individual assignments, most of which being expendable. + + +## Motivation +As of today, the only way to "dynamically" assign table values is via `unpack`/`select` and variable list assignments. With that being said, these functions cannot be used on dictionaries and limit control over what values are being retrieved. + +With the introduction of destructuring assignments, we'll be able to substantially reduce redundant variables, index calls, and bloat in our code. Instead, using a clean syntax that allows the intuitive selection and assignment of values from arrays and dictionaries. + +Destructuring allows for shortened code with increased flexibility. The expected outcome__s__ are endless. + +```lua +-- Example using HTTP Service +-- Based on example from https://developer.roblox.com/en-us/api-reference/class/HttpService + +local response = HttpSevice:GetAsync("http://api.open-notify.org/astros.json") +local data = HttpService:JSONDecode(response) + +local { message, number, people } = data + +if message == "success" then + print("There are currently", data.number, "astronauts in space:") + for i, { name, craft } in people do + print(i .. ": " .. name .. " is on " .. craft) + end +end +``` +```lua +-- Example using Fusion + +local { New, Children } = Fusion + +return New "ScreenGui" { + -- ... +} +``` +```lua +-- Example using a RemoteFunction callback + +RemoteFunction.InvokeServer = function(player, { target, damage }) + print("Dealt " .. damage .. " to " .. target.Name) +end +``` + +## Design + +### Variable Assignments for Arrays +With the ability to use `unpack` to destructure arrays, the need for this feature can seem negligible. Nevertheless, `unpack` cannot be used on dictionaries. +```lua +-- Current +local foo = {1, 2, 3} + +local one, two, three = unpack(foo) -- 1, 2, 3 +``` +```lua +-- New +local foo = {1, 2, 3} + +local {one, two, three} = foo -- 1, 2, 3 +``` + +### Variable Assignments for Dictionaries +```lua +-- Current +local foo = { + red = "good"; + green = "better"; + blue = "best"; +} + +local red, green, blue = foo.red, foo.green, foo.blue +print(red) -- good +print(green) -- better +print(blue) -- best +``` +```lua +-- New +local foo = { + red = "good"; + green = "better"; + blue = "best"; +} + +local {red, green, blue} = foo +print(red) -- good +print(green) -- better +print(blue) -- best +``` + +### Variable Assignments for Function Returns +```lua +-- Current +local function f() + return { + name = "John"; + age = 25; + gender = "male"; + } +end + +local data = f() +local name, age, gender = data.name, data.age, data.gender + +print(name) -- John +print(age) -- 25 +print(gender) -- male +``` +```lua +-- New +local function f() + return { + name = "John"; + age = 25; + gender = "male"; + } +end + +local {name, age, gender} = f() +print(name) -- John +print(age) -- 25 +print(gender) -- male +``` + +### Variable Assignments for UserData & Vectors +```lua +-- Current +local Part = workspace.Part +local Position = Part.CFrame.Position +local X, Y, Z = Position.X, Position.Y, Position.Z + +print(X, Y, Z) -- 0, 0, 0 +``` +```lua +-- New +local Part = workspace.Part +local {Position} = Part.CFrame +local {X, Y, Z} = Position + +print(X, Y, Z) -- 0, 0, 0 +``` + +### Variable Assignments for Indexing Children (???) +```lua +-- Current +local Baseplate = workspace.Baseplate +print(Baseplate) -- Baseplate +``` +```lua +-- New +local {Baseplate} = workspace +print(Baseplate) -- Baseplate +``` + +--- + +### Destructuring Function Parameters +```lua +-- Before +local function f(data) + local name = data.name + local age = data.age + local gender = data.gender + + print(name, age, gender) -- John, 25, male +end + +f({ + name = "John"; + age = 25; + gender = "male"; +}) +``` +```lua +-- New +local function f({ name, age, gender }) + print(name, age, gender) -- John, 25, male +end + +f({ + name = "John"; + age = 25; + gender = "male"; +}) +``` + +--- + +### Defining Alternative Identifiers (Questionable?) +In this example, `fizz-buzz` is not a valid identifier. You should be able to instead define an alternative identifier, like so. +```lua +local foo = { + ["fizz-buzz"] = true; +} + +local { fizzbuzz = "fizz-buzz" } = foo +print(fizzbuzz) -- fizz-buzz +``` + +### More Complex Example +This example is a direct port of the JavaScript example I gave on issue #617. +```lua +local obj = { + name = "John"; + records = { + { + date = "7/29/2022"; + text = "Hello World!"; + } + } +} + +local { + name, + records = { + { text } + } +} = obj + +print(name .. " says " .. text) -- John says Hello World! +``` + +## Drawbacks + +From my perspective, this would be a rather large addition to the language. *Some* of the drawbacks are listed below: +* Increased compiler complexity +* Considered language bloat +* New, unknown syntax +* Not necessarily friendly to new programmers + +With that being said, these drawbacks are all subjective, and likely won't matter to people who choose not to use destructuring in their code. + +## Alternatives + +The only other alternatives are `unpack`/`select`. If this weren't implemented, it would just lead to longer code.