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

Pattern and match expression syntax #100

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

Almost89
Copy link

This is my first time contributing to the language and RFC process, if there are any mistakes or I've missed something please let me know.

Rendered

@Wunder-Wulfe
Copy link

Wunder-Wulfe commented Feb 23, 2025

I believe that Luau could benefit more from a native implementation of complex Regular Expressions, either with a special literal like /pattern/flags (unambiguous due to the leading slash, as otherwise you would use parenthesis (2/./ would be a division-related syntax error, 2(/./) would be a function-call related syntax error)), as a string like "pattern", or as an object like regexp("pattern", "flags"), as I feel like complex regular expressions are already feature-complete and would provide more options for matching things without additional discrepancies across other languages, and would match the grammar structures used in most parsers.

@jackdotink
Copy link
Contributor

@Wunder-Wulfe
IIRC A typical regex implementation has a larger binary size than the entirety of Luau. It should not be added to the language.

@Almost89
Copy link
Author

@Wunder-Wulfe
I think I agree with Jack on not adding Regex to Luau. We could explore adding a string pattern utilizing Luau's existing string matching syntax though.

I can't think of a syntax for it right now but if the following was a string pattern (instead of an exact):

"%d+"

It would be equivalent to type(value) == "string" and string.match(value, "%d+") ~= nil.

@deviaze
Copy link

deviaze commented Feb 23, 2025

I generally like the semantics proposed, however I'm not quite convinced on the proposed syntax and the value-add for Luau. In general, I think the RFC needs to more clearly explain why match expressions are an improvement over the if-else expressions we currently have in Luau.

Syntax-wise,

No parens

We don't require parentheses/punctuation anywhere else to denote the start/end of blocks/exprs/control flow, so we shouldn't here.

for keyword

I don't think for is the right choice here, as it moreso implies a for loop or a table comprehension (future RFC I've been working on!) than a match expression.

else instead of *

I think the catch-all case should be a required trailing else to match the existing behavior of if expressions. Required else branches were an intentional decision in the design of if-else expressions, and I believe the same arguments should apply to match expressions as well.

=>

At the risk of provoking a bikeshed, I would prefer => over -> to separate patterns and exprs; -> is already used within the type system and we should distinguish type system syntax from runtime syntax. Plus, it's the token used in Rust, so it's more familiar to users who've used match expressions before.

Proposal: match <exprbutnotastringliteral> in <cases> => <exprs>, else <expr>

Putting the match contextual keyword in front improves consistency with if expressions (match-in-else vs if-then-else) and better conveys programmer intent. The tradeoff here is that we can no longer match against string literals (except for interpolated strings) because match "meow" is a valid functioncall. But honestly, I don't see why you'd want to match against a string literal you just wrote??, so I think it's okay to disallow that.

With that out of the way, here's what I think match expressions could look like:

local function parse_simple_expr(): AstExprNode
    return match current_token.kind in
        "string" => parse_string_expr(),
        "number" => parse_number_expr(),
        "true" or "false" => parse_boolean_expr(),
    else error(`unexpected token {current_token.kind} "{current_token.text}" {current_token.span.x}:{current_token.span.y}`)
end

I feel this looks a lot cleaner, more readable, and more consistent with the rest of Luau. But you can also clean it up with a pretty-readable if-expression as well:

local function parse_simple_expr(): AstExprNode
    local kind = current_token.kind
    return 
        if kind == "string" then 
           parse_string_expr()
        elseif kind == "number" then 
           parse_number_expr()
        elseif (kind == "true" or kind == "false") then
           parse_boolean_expr()
        else 
           error(`unexpected token {kind} "{current_token.text}" {current_token.span.x}:{current_token.span.y}`)
end

Value proposition

In Rust, match expressions are extremely powerful as they're used to destructure enums, introduce bindings, cleanly handle Ok/Err cases, etc., and are an integral part of the language. This isn't the case in Luau, as we don't have the same usecases for match expressions that Rust does. They could be useful, but I'm not sure they meet the high bar that Luau requires for such a major runtime control flow feature.

In Luau, I feel like the best usecase for match expressions would be in ergonomic table destructuring -- without which match expressions might not provide much more value than if-else expressions (except for decreased verbosity).

Anyway, good effort on the RFC. I really like Rust's match expressions and I do miss them in Luau. I just want to make this RFC a bit stronger! Please let me know your thoughts on these proposed changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants