Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: or return syntax #53

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions docs/or-return-syntax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# `or return` Syntax

## Summary

The `or return` syntax is a more compact, understandable way to end execution if an expression evaluates to `nil`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent with usual truthiness test. What if the expression evaluates to false?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should have clarified here - the main use case is for expressions that can return nil, but the already-established truthiness rules would be respected.


## Motivation

Existing ways of handling errors or `nil` values in Luau tend to be either verbose or dense. Code with nested `if` statements can make indents unnecessarily large, which hinders readability. Checks after variable initialization are better, but still make code lengthy and annoying to write.

`or return` would take this common idiom:
```lua
local var = foo()
if not var then return false end
```
and compact it into a single, simpler statement:
```lua
local var = foo() or return false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't compose with multrets.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? As far as I can tell, the only possible reason it wouldn't is if commas were used somewhere else in the expression, which is solved by using a pair of parentheses.

```

This idiom in various forms is omnipresent in nearly all Luau code, with prime examples occuring in Roblox development docs. The [second scripting tutorial](https://create.roblox.com/docs/tutorials/scripting/basic-scripting/deadly-lava) has this piece of code:
```lua
local lava = script.Parent

local function kill(otherPart)
local partParent = otherPart.Parent
local humanoid = partParent:FindFirstChild("Humanoid")
if humanoid then
humanoid.Health = 0
end
end

lava.Touched:Connect(kill)
```
With this RFC implemented, the code could shrink to this, which I personally feel is more understandable at a glance:
```lua
local lava = script.Parent

local function kill(otherPart)
local partParent = otherPart.Parent
local humanoid = partParent:FindFirstChild("Humanoid") or return

humanoid.Health = 0
Comment on lines +41 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just if local which is proposed in a different RFC, so this isn't a strong motivator.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if local does also solve this problem, but it also creates a new indentation level which is counter to the goals of this RFC.

end

lava.Touched:Connect(kill)
```

## Design

`or return` can be implemented as a binary operation with an optional right side, returning `nil` if no value is given.

The parsing step would be fairly simple as well. Since the new syntax is a way to express an already-supported feature more concisely, it can be transformed back into its original form in the parser. This means no changes are necessitated on the compiler side. If specified in the middle of an expression, it can temporarily store the value on its left side to perform the nil check, then compute the rest of the expression using the original copy. For example,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parser can't rewrite the AST beyond its local context, so AST changes are required for this feature to work.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's understandable. Is there some form of AST expansion/lowering pass that this could fit into, or would this need to be supported in the compiler as well?

```lua
local var = bar(foo or return)
```
would be expanded into
```lua
local tmp = foo
if not foo then return end
local var = bar(foo)
```
which would preserve the existing behavior of expressions, while avoiding the need for compiler or AST changes.

## Drawbacks

Implementing this as an operation might make parsing harder, especially since `or return` overlaps with `or`. The optional right argument could also be an issue in more complex expressions, though giving the operation a low priority could help by nudging the user towards grouping statements with parentheses.

## Alternatives

There are already alternatives built into the language - this is purely a convenience feature. Regardless, the other options are more effort to write, which I believe means this shortcut option is worthy of consideration.