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: switch for statements #63

Open
wants to merge 3 commits 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
55 changes: 55 additions & 0 deletions docs/switch-for-statements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# switch for statements

## Summary

This RFC proposes a new `switch` syntax for the Luau language, adding `switch` statements for clearer, more concise branching logic.

## Motivation

The purpose of the `switch` statement is to simplify readability in cases where multiple branches depend on the value of one variable. Luau currently supports branching by using chains of `if`-`elseif`, but these may become wordy if multiple values are to be checked, or if fall-through behavior is desired. A `switch` syntax allows a simpler, easier to read structure.
Copy link
Contributor

@alexmccord alexmccord Oct 28, 2024

Choose a reason for hiding this comment

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

if fall-through behavior is desired

There are many options available to you for implementing explicit fallthrough semantics if it is desired, which is arguably better than the language construct doing it for you.

One model you can steal from is user input events, where a chain of functions are invoked with the same value. If the value matches some pattern, do something. Even if the handler has "handled" that input, they can still choose to pass through or sink the event. Sounds familiar?

type HandlerResult = "pass" | "sink" | nil -- nil == "pass"
type Handler<T> = (T) -> HandlerResult
type HandlerChain<T> = {Handler<T>}


## Design

The syntax below, now proposed, is using `switch value`, then the definition of cases with `for` blocks. Each case can take a tuple of comma-separated values; its code is inside `do` and `end`. A default case is a `do` block at the end.

Example:

```lua
switch value
for "a" do
-- Code for case "a"
end
for "b" do
-- Code for case "b"
break
Copy link
Contributor

Choose a reason for hiding this comment

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

Fallthrough semantics in a switch is considered a mistake, begs a question wrt scoping rules e.g. in case 1 that falls through to case 2, are locals introduced in case 1 also visible to case 2?

end
for "a", "b", "c" do
Copy link
Contributor

Choose a reason for hiding this comment

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

This reads like it's for multiple returns. "a" | "b" | "c" would have been a better syntactic choice here.

-- Code for any of these values
break
end
do
-- Code for default case
end
end
```

## Drawbacks

This syntax adds a pattern not found in Luau, and may necessitate concepts for developers to learn. The syntax looks like the syntax of `for` loops but isn't a loop, which can be confusing in some cases.

## Alternatives

For instance, in the absence of a syntax for `switch`, developers depend on `if`-`elseif` chains: In the absence of a syntax for `switch`, developers have to depend on `if`-`elseif` chains, which often become cumbersome and harder to read with multiple values to check. To that effect, consider handling the fall-through behavior of `switch` statements. Here every condition is explicitly repeated; thus, code becomes more verbose and error-prone:
Copy link
Contributor

Choose a reason for hiding this comment

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

This repeats the premise twice?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would also argue that the switch version is more error-prone, because consequent cases may potentially have to deal with conditions from the antecedent cases, depending on fallthrough, which may not always exist syntactically, i.e. you may call some function that itself could then throw an error.


```lua if value == "a" then
-- Code for case "a"
elseif value == "b" then
-- Code for case "b"
elseif value == "c" or value == "d" then
-- Code for case "c" or "d"
else
-- Default case
end
```

Using the above chained `if`s, the convenience a `switch` provides is consumed by repeated comparisons and explicit coding of fall-through behavior. This can result in longer branching code and is more annoying to maintain when cases are added or altered.