mirror of
https://github.com/luau-lang/luau.git
synced 2025-05-04 10:33:46 +01:00
Remove references to calling
This commit is contained in:
parent
0c168f8ccd
commit
23770cb759
1 changed files with 13 additions and 61 deletions
|
@ -26,79 +26,24 @@ local dogName = dog and dog.name
|
||||||
|
|
||||||
...which will return `false` if `dog` is `false`, instead of throwing an error because of the index of `false.name`.
|
...which will return `false` if `dog` is `false`, instead of throwing an error because of the index of `false.name`.
|
||||||
|
|
||||||
This trick gets worse in the case of calling methods. For example, let's suppose we wanted to call `dog.fetch()`, while `dog` is still potentially nil.
|
Luau provides the if...else expression making this turn into:
|
||||||
|
|
||||||
The one-line `and` trick will no longer work (or will get less readable as you try to shoe-horn this in), so we must:
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
if dog ~= nil then
|
local dogName = if dog == nil then nil else dog.name
|
||||||
dog.fetch()
|
|
||||||
end
|
|
||||||
```
|
```
|
||||||
|
|
||||||
But this gets even worse when it comes to chained indexing. Let's suppose we wanted to run `dog.owner.handshake()`, while `dog` can be nil and `owner` can be nil.
|
...but this is fairly clunky for such a common expression.
|
||||||
|
|
||||||
```lua
|
|
||||||
if dog ~= nil and dog.owner ~= nil then
|
|
||||||
dog.owner.handshake()
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
...which gets even worse in the context of calling this function in, say, another function:
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- Oops! dog and dog.owner can be nil
|
|
||||||
logDogName(getLogger(), dog.name, dog.owner:getDisplayName())
|
|
||||||
|
|
||||||
-- In order to preserve this order (assuming argument execution order mattered)...
|
|
||||||
local logger = getLogger()
|
|
||||||
local name = dog and dog.name
|
|
||||||
local displayName
|
|
||||||
|
|
||||||
if dog.owner ~= nil then
|
|
||||||
displayName = dog.owner:getDisplayName()
|
|
||||||
end
|
|
||||||
|
|
||||||
logDogName(logger, name, displayName)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
The safe navigation operator will make all of these smooth, by supporting `x?.y` and similar indexing operators. `dog?.name` would resolve to `nil` if `dog` was nil, or the name otherwise. `owner?.handshake()` would only call `handshake` if `owner` is not nil.
|
The safe navigation operator will make all of these smooth, by supporting `x?.y` to safely index nil values. `dog?.name` would resolve to `nil` if `dog` was nil, or the name otherwise.
|
||||||
|
|
||||||
The long example would turn into:
|
The previous example turns into `local dogName = dog?.name` (or just using `dog?.name` elsewhere).
|
||||||
|
|
||||||
```lua
|
|
||||||
logDogName(getLogger(), dog?.name, dog?.owner?:getDisplayName())
|
|
||||||
```
|
|
||||||
|
|
||||||
Failing the nil-safety check early would make the entire expression nil, for instance `dog?.body.legs` would resolve to `nil` if `dog` is nil, rather than resolve `dog?.body` into nil, then turning into `nil.legs`.
|
Failing the nil-safety check early would make the entire expression nil, for instance `dog?.body.legs` would resolve to `nil` if `dog` is nil, rather than resolve `dog?.body` into nil, then turning into `nil.legs`.
|
||||||
|
|
||||||
The list of valid operators to follow the safe navigation operator would be:
|
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
dog?.name --[[ is the same as ]] if dog == nil then nil else dog.name
|
dog?.name --[[ is the same as ]] if dog == nil then nil else dog.name
|
||||||
dog?.getName() --[[ is the same as ]] if dog == nil then nil else dog.getName()
|
|
||||||
dog?:getName(args) --[[ is the same as ]] if dog == nil then nil else dog:getName(args)
|
|
||||||
```
|
|
||||||
|
|
||||||
When using safe navigation to call a function, the short circuiting will prevent the arguments from being evaluated in the case of nil.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- Will NOT call getValue() if `object` is nil
|
|
||||||
object?.doSomething(getValue())
|
|
||||||
```
|
|
||||||
|
|
||||||
This will return one `nil` if the object was nil.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function Class:returnTwoValues()
|
|
||||||
return 1, 2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Assuming `object` is `Class`, and `nilObject` is nil
|
|
||||||
print(object?:returnTwoValues()) -- 1, 2
|
|
||||||
print(nilObject?:returnTwoValues()) -- nil
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The short-circuiting is limited within the expression.
|
The short-circuiting is limited within the expression.
|
||||||
|
@ -131,6 +76,14 @@ local nonOptionalObject: { name: string }
|
||||||
local nonOptionalObjectName = nonOptionalObject?.name -- resolves to `string`
|
local nonOptionalObjectName = nonOptionalObject?.name -- resolves to `string`
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Calling
|
||||||
|
|
||||||
|
This RFC only specifies `x?.y` as an index method. `x?:y()` is currently unspecified, and `x?.y(args)` as a syntax will be reserved (will error if you try to use it).
|
||||||
|
|
||||||
|
While being able to support `dog?.getName()` is useful, it provides [some logistical issues for the language](https://github.com/Roblox/luau/pull/142#issuecomment-990563536).
|
||||||
|
|
||||||
|
`x?.y(args)` will be reserved both so that this can potentially be resolved later down the line if something comes up, but also because it would be a guaranteed runtime error under this RFC: `dog?.getName()` will first index `dog?.getName`, which will return nil, then will attempt to call it.
|
||||||
|
|
||||||
### Assignment
|
### Assignment
|
||||||
`x?.y = z` is not supported, and will be reported as a syntax error.
|
`x?.y = z` is not supported, and will be reported as a syntax error.
|
||||||
|
|
||||||
|
@ -147,4 +100,3 @@ Doing nothing is an option, as current standard if-checks already work, as well
|
||||||
Supporting optional calls/indexes, such as `x?[1]` and `x?()`, while not out of scope, are likely too fringe to support, while adding on a significant amount of parsing difficulty, especially in the case of shorthand function calls, such as `x?{}` and `x?""`.
|
Supporting optional calls/indexes, such as `x?[1]` and `x?()`, while not out of scope, are likely too fringe to support, while adding on a significant amount of parsing difficulty, especially in the case of shorthand function calls, such as `x?{}` and `x?""`.
|
||||||
|
|
||||||
It is possible to make `x?.y = z` resolve to only setting `x.y` if `x` is nil, but assignments silently failing can be seen as surprising.
|
It is possible to make `x?.y = z` resolve to only setting `x.y` if `x` is nil, but assignments silently failing can be seen as surprising.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue