mirror of
https://github.com/luau-lang/rfcs.git
synced 2025-05-04 10:43:48 +01:00
Add syntax-shorthand-field-init.md
This commit is contained in:
parent
85b4d9a9d9
commit
ebfdcee8d8
1 changed files with 157 additions and 0 deletions
157
docs/syntax-shorthand-field-init.md
Normal file
157
docs/syntax-shorthand-field-init.md
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
# Shorthand field initialization
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
This RFC proposes a shorthand syntax for declaring fields in tables, which allows writing
|
||||||
|
`.fieldName` instead of `fieldName = fieldName` when the value of the field is being set to a
|
||||||
|
variable of the same name as the field.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
It is very common for the variable which you assign as the value of a field in a table to have the
|
||||||
|
same name as said field.
|
||||||
|
|
||||||
|
In these cases, you end up writing the name of the variable at least three times. The following
|
||||||
|
block of code illustrates an example of this.
|
||||||
|
```lua
|
||||||
|
local str = ":)"
|
||||||
|
local tbl = {
|
||||||
|
str = str,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This repetition makes it less ergonomic to declare *variables* which you assign to
|
||||||
|
fields, as opposed to *embedding* the value of those variables directly in the initialization of the
|
||||||
|
field. As an example of this, the above code snippet could be written as the following:
|
||||||
|
```lua
|
||||||
|
local tbl = {
|
||||||
|
str = ":)",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This modification is often not ideal. For example, if the computation of the value of some fields
|
||||||
|
depends on the value of other fields, or if the expression which would be used instead of the
|
||||||
|
variable in the field initialization is too complex. Another example is module exports.
|
||||||
|
|
||||||
|
A module exporting some function `init` *may* look like the following:
|
||||||
|
```lua
|
||||||
|
local function init()
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.freeze({
|
||||||
|
init = init,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
As you can see, the one field's variable (`init`) has to be written at least three times. Using a
|
||||||
|
different method of exporting variables from modules, you may end up with the following code:
|
||||||
|
```lua
|
||||||
|
local module = {}
|
||||||
|
|
||||||
|
function module.init()
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.freeze(module)
|
||||||
|
```
|
||||||
|
Using this method, the name of the returned table needs to be written (2 + n) times, where n is the
|
||||||
|
number of variables you export. Every exported variable also needs to be written at least one or
|
||||||
|
three times. Potentially three times because you may need to localize exported variables before
|
||||||
|
adding them to the returned table. An example of this would be the following.
|
||||||
|
```lua
|
||||||
|
local module = {}
|
||||||
|
|
||||||
|
export type TypeWhichSomeExportedVariableHas = {
|
||||||
|
str: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- localize first so that we can specify the type of `SOME_EXPORTED_VARIABLE`.
|
||||||
|
local SOME_EXPORTED_VARIABLE: TypeWhichSomeExportedVariableHas = {
|
||||||
|
str = ":)",
|
||||||
|
}
|
||||||
|
|
||||||
|
module.SOME_EXPORTED_VARIABLE = SOME_EXPORTED_VARIABLE
|
||||||
|
|
||||||
|
return table.freeze(module)
|
||||||
|
```
|
||||||
|
So, this method may (arguably) be more ergonomic or less ergonomic than the method which first was
|
||||||
|
mentioned. However, using shorthand field initialization syntax instead, it could be written like
|
||||||
|
this:
|
||||||
|
```lua
|
||||||
|
export type TypeWhichSomeExportedVariableHas = {
|
||||||
|
str: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
local SOME_EXPORTED_VARIABLE: TypeWhichSomeExportedVariableHas = {
|
||||||
|
str = ":)",
|
||||||
|
}
|
||||||
|
|
||||||
|
return table.freeze({
|
||||||
|
.init,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
Which (arguably) is better.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### Grammar
|
||||||
|
|
||||||
|
The proposed grammar expands the `field` rule to the following:
|
||||||
|
```
|
||||||
|
field = '[' exp ']' '=' exp | NAME '=' exp | exp | '.' NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
For some module exporting a type, `Phone`, and a function, `init`, which creates a table of type
|
||||||
|
`Phone`, the proposed syntax may be used in the following way:
|
||||||
|
```lua
|
||||||
|
export type Phone = {
|
||||||
|
number: number,
|
||||||
|
owner: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function init(number, owner): Phone
|
||||||
|
return {
|
||||||
|
-- NOTE: The following line of code may be written in a number of different, but equivalent
|
||||||
|
-- ways. For example:
|
||||||
|
-- * .number;
|
||||||
|
-- * number = number,
|
||||||
|
-- * number = number;
|
||||||
|
.number,
|
||||||
|
-- NOTE: The following line of code may be written in a number of different, but equivalent
|
||||||
|
-- ways. For example:
|
||||||
|
-- * .owner;
|
||||||
|
-- * .owner (since it's the last element)
|
||||||
|
-- * owner = owner,
|
||||||
|
-- * owner = owner;
|
||||||
|
-- * owner = owner (since it's the last element)
|
||||||
|
.owner,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.freeze({
|
||||||
|
-- NOTE: The following line of code may be written in a number of different, but equivalent
|
||||||
|
-- ways. For example:
|
||||||
|
-- * .init;
|
||||||
|
-- * .init (since it's the last element)
|
||||||
|
-- * init = init,
|
||||||
|
-- * init = init;
|
||||||
|
-- * init = init (since it's the last element)
|
||||||
|
.init,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Drawbacks
|
||||||
|
|
||||||
|
Introducing this shorthand syntax adds a small level of complexity to the language, which
|
||||||
|
potentially is confusing to newcomers. It also occupies syntax which might preferably be used for
|
||||||
|
other features in the future.
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
The added grammar to the `field` rule needs to have a prefix included, since it otherwise would be
|
||||||
|
indistinguishable from initialization of array-like table fields, but there are alternatives for
|
||||||
|
what that prefix may be.
|
||||||
|
|
||||||
|
Additionally, if records are introduced in the future, this type of syntax could be used for them
|
||||||
|
exclusively, and without a prefix, arguably making code look "cleaner".
|
Loading…
Add table
Reference in a new issue