-
Notifications
You must be signed in to change notification settings - Fork 92
Add RFC: match expression syntax #197
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,324 @@ | ||||||||||||||||||||||||||||||||||||||||
| # Match Expression | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Summary | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| This RFC proposes a `match` expression for Luau. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| The goal is to make multi-branch value selection clearer when logic depends on one or more values and branch order matters. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| The construct supports: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - matching against one or more values | ||||||||||||||||||||||||||||||||||||||||
| - positional patterns for multi-value matching | ||||||||||||||||||||||||||||||||||||||||
| - wildcard `_` patterns | ||||||||||||||||||||||||||||||||||||||||
| - expression-list results (including multiple return values) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| This is intended as a structured alternative to repeated `if` / `elseif` comparisons in expression position. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Motivation | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Luau already supports `if` expressions: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local result = if a == 1 then | ||||||||||||||||||||||||||||||||||||||||
| 5 | ||||||||||||||||||||||||||||||||||||||||
| elseif b == 2 then | ||||||||||||||||||||||||||||||||||||||||
| 6 | ||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||
| 7 | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| For single conditions this is fine. For multi-value branching, conditions become repetitive and less readable: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local result | ||||||||||||||||||||||||||||||||||||||||
| if a == 1 and b == 0 then | ||||||||||||||||||||||||||||||||||||||||
| result = 5 | ||||||||||||||||||||||||||||||||||||||||
| elseif a == 0 and b == 2 then | ||||||||||||||||||||||||||||||||||||||||
| result = 6 | ||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||
| result = 7 | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| The compared values (`a`, `b`) repeat, and branch priority is spread across boolean logic. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| A `match` expression centralizes compared values once and expresses branches positionally. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| A concrete case is leap-year logic: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local mod400 = year % 400 | ||||||||||||||||||||||||||||||||||||||||
| local mod100 = year % 100 | ||||||||||||||||||||||||||||||||||||||||
| local mod4 = year % 4 | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| local isLeapYear = match mod400, mod100, mod4 with | ||||||||||||||||||||||||||||||||||||||||
| if 0, _, _ then true | ||||||||||||||||||||||||||||||||||||||||
| elseif _, 0, _ then false | ||||||||||||||||||||||||||||||||||||||||
| elseif _, _, 0 then true | ||||||||||||||||||||||||||||||||||||||||
| else false | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| This directly encodes the precedence rules: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - divisible by 400 -> leap year | ||||||||||||||||||||||||||||||||||||||||
| - divisible by 100 -> not leap year | ||||||||||||||||||||||||||||||||||||||||
| - divisible by 4 -> leap year | ||||||||||||||||||||||||||||||||||||||||
| - otherwise -> not leap year | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ## Design | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### Syntax | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| The syntax takes its inspirations from OCaml. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Canonical form: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| match <expr-list> with | ||||||||||||||||||||||||||||||||||||||||
| if <pattern-list> then <expr-list> | ||||||||||||||||||||||||||||||||||||||||
| elseif <pattern-list> then <expr-list> | ||||||||||||||||||||||||||||||||||||||||
| else <expr-list> | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Rules: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - `<expr-list>` is one or more expressions separated by `,` | ||||||||||||||||||||||||||||||||||||||||
| - `<pattern-list>` is one or more patterns separated by `,` | ||||||||||||||||||||||||||||||||||||||||
| - each branch must provide exactly one pattern per matched expression | ||||||||||||||||||||||||||||||||||||||||
| - in this revision, patterns are limited to literals and `_` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### Examples | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Single value: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local speed = match state with | ||||||||||||||||||||||||||||||||||||||||
| if "Idle" then 0 | ||||||||||||||||||||||||||||||||||||||||
| elseif "Run" then 16 | ||||||||||||||||||||||||||||||||||||||||
| else 8 | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Multiple values: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local value = match a, b with | ||||||||||||||||||||||||||||||||||||||||
| if 1, 0 then 5 | ||||||||||||||||||||||||||||||||||||||||
| elseif 0, 2 then 6 | ||||||||||||||||||||||||||||||||||||||||
| else 7 | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Wildcard: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local value = match a, b with | ||||||||||||||||||||||||||||||||||||||||
| if 1, _ then 5 | ||||||||||||||||||||||||||||||||||||||||
| elseif _, 2 then 6 | ||||||||||||||||||||||||||||||||||||||||
| else 7 | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Multiple return values: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local x, y = match a, b with | ||||||||||||||||||||||||||||||||||||||||
| if 5, _ then "A", "B" | ||||||||||||||||||||||||||||||||||||||||
| elseif _, 5 then "B", "A" | ||||||||||||||||||||||||||||||||||||||||
| else "C", "C" | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Use in return position: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local function f(x) | ||||||||||||||||||||||||||||||||||||||||
| return match x with | ||||||||||||||||||||||||||||||||||||||||
| if "a" then "b" | ||||||||||||||||||||||||||||||||||||||||
| else "c" | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### Semantics | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - Match input expressions are evaluated once, left-to-right. | ||||||||||||||||||||||||||||||||||||||||
| - Cases are tested top-to-bottom. | ||||||||||||||||||||||||||||||||||||||||
| - A literal pattern matches by equality (`==`). | ||||||||||||||||||||||||||||||||||||||||
| - `_` matches any value and does not bind. | ||||||||||||||||||||||||||||||||||||||||
| - The first matching case is selected. | ||||||||||||||||||||||||||||||||||||||||
| - If no case matches, the `else` case is used when present. | ||||||||||||||||||||||||||||||||||||||||
| - If no case matches and `else` is omitted, this is a runtime error. | ||||||||||||||||||||||||||||||||||||||||
| - The selected case yields an expression list, which is the value of the `match` expression. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| For multi-value matching: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| match a, b with | ||||||||||||||||||||||||||||||||||||||||
| if p1, p2 then ... | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| is equivalent to checking: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| a == p1 and b == p2 | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| with `_` acting as an always-true position. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### Type Checking | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| `match` integrates with existing branch-based analysis. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Literal narrowing (single value): | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| type State = "Idle" | "Walk" | "Run" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| local speed = match state with | ||||||||||||||||||||||||||||||||||||||||
| if "Idle" then 0 | ||||||||||||||||||||||||||||||||||||||||
| elseif "Walk" then 8 | ||||||||||||||||||||||||||||||||||||||||
| elseif "Run" then 16 | ||||||||||||||||||||||||||||||||||||||||
| else 0 | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Within each branch, matched values may be narrowed to literal-compatible types. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Multi-value narrowing: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| type A = 1 | 2 | ||||||||||||||||||||||||||||||||||||||||
| type B = 3 | 4 | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| local result = match a, b with | ||||||||||||||||||||||||||||||||||||||||
| if 1, 3 then "x" | ||||||||||||||||||||||||||||||||||||||||
| elseif 2, 4 then "y" | ||||||||||||||||||||||||||||||||||||||||
| else "z" | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Each position can narrow independently. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Exhaustiveness: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - When patterns are literals over finite types, implementations may report non-exhaustive matches if `else` is omitted. | ||||||||||||||||||||||||||||||||||||||||
| - `else` suppresses exhaustiveness diagnostics. | ||||||||||||||||||||||||||||||||||||||||
| - `_` in all remaining uncovered positions behaves as a catch-all. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Wildcard behavior: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - `_` does not introduce bindings and does not itself narrow matched values. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Result type: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| - The result type is the union of branch result types. | ||||||||||||||||||||||||||||||||||||||||
| - For multi-value returns, branches should have consistent arity when statically known. | ||||||||||||||||||||||||||||||||||||||||
| - If arity differs, implementation may report an error or apply existing Luau multi-return behavior. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ### Desugaring (Conceptual) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| match a, b with | ||||||||||||||||||||||||||||||||||||||||
| if 1, _ then x | ||||||||||||||||||||||||||||||||||||||||
| elseif _, 2 then y | ||||||||||||||||||||||||||||||||||||||||
| else z | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| is conceptually equivalent to: | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| ```lua | ||||||||||||||||||||||||||||||||||||||||
| local __m1, __m2 = a, b | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| if __m1 == 1 then | ||||||||||||||||||||||||||||||||||||||||
| x | ||||||||||||||||||||||||||||||||||||||||
| elseif __m2 == 2 then | ||||||||||||||||||||||||||||||||||||||||
| y | ||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||
| z | ||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+236
to
+243
|
||||||||||||||||||||||||||||||||||||||||
| if __m1 == 1 then | |
| x | |
| elseif __m2 == 2 then | |
| y | |
| else | |
| z | |
| end | |
| local __result | |
| if __m1 == 1 then | |
| __result = x | |
| elseif __m2 == 2 then | |
| __result = y | |
| else | |
| __result = z | |
| end | |
| __result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grammar nit: “takes its inspirations from OCaml” is unidiomatic; consider “takes inspiration from OCaml” or “is inspired by OCaml”.