Syntax...:Match(3pm)

πŸ’– NAME πŸ’–

Syntax::Keyword::Match β€” a match/case syntax for perl πŸŽ€


✨ SYNOPSIS ✨

use v5.16;
use Syntax::Keyword::Match;

my $n = ...;

match($n : ==) {
   case(1) { say "It's one" }
   case(2) { say "It's two" }
   case(3) { say "It's three" }
   case(4), case(5)
           { say "It's four or five" }
   case if($n < 10)
           { say "It's less than ten" }
   default { say "It's something else" }
}

🌸 DESCRIPTION 🌸

okay bestie so listen πŸ’… this module is a syntax plugin that gives you a whole control-flow block called match/case. What does it do? It runs at most one of a whole lineup of different blocks, depending on what its controlling expression evaluates to. Like a lil bouncer at the club door picking exactly ONE person to let through. πŸ‘‘

It's giving very C's switch/case energy (that syntax got copied into like, a million other languages ngl), or the vibes of Switch::Plain. πŸ¦‹

⚠️ real talk though: this is an initial, experimental implementation. And on top of that, it's built as a non-trivial example use-case on top of XS::Parse::Keyword, which is also experimental. No API or compatibility guarantees are made at this time. fr fr, no cap. πŸ’€

πŸ§ͺ Experimental Features πŸ§ͺ

Some of the features in here are marked as experimental (yes, even within the context that the whole module is experimental, we're doing layers here 🫢). They WILL provoke warnings in the experimental category, unless you silence them.

use Syntax::Keyword::Match qw( match :experimental(dispatch) );

use Syntax::Keyword::Match qw( match :experimental );  # all of the above

πŸ”‘ KEYWORDS πŸ”‘

πŸ‘‘ match

match( EXPR : OP ) {
   ...
}

A match statement hands over three things: the controlling expression, the comparison operator, and a lil sequence of case statements for the match operation. The expression gets evaluated to yield a scalar value, and then that value gets compared β€” using the comparison operator β€” against each of the case labels in the order they are written, topmost first. ✨

If a match is found, the body of that labelled block runs. If nothing matches but a default block is chillin' there, that's the one that runs instead. And after a single inner block has been executed, no further tests are performed and execution just continues from the statement following the match statement. periodt. πŸ’…

🚨 heads up: the braces following the match block must ONLY contain case or default statements. Arbitrary code is not supported here. Not even a little. πŸ™ˆ

Even though a match statement is a full statement and not an expression, it can still yield a value if it appears as the final statment in its containing sub or do block. Obsessed honestly. For example:

my $result = do {
   match( $topic : == ) {
      case(1) { ... }
   }
};

And if the controlling expression introduces a new variable, that variable will be visible within any of the case blocks, and will go out of scope after the match statement finishes. Super useful for temporarily stashing the result of a more complex expression πŸ’–

match( my $x = some_function_call() : == ) {
   case ...
}

πŸŽ€ Comparison Operators πŸŽ€

The comparison operator must be either eq (comparing cases as strings), or == (comparing them as numbers), or =~ (comparing cases using regexps).

Since version 0.11 on any Perl release, or previous versions on Perl releases 5.32 onwards, the isa operator is also supported β€” so you can dispatch based on what type of object the controlling expression gives you. main character energy for real. 🌈

match( $obj : isa ) {
   case(A::Package)       { ... }
   case(Another::Package) { ... }
}
🌸 bestie remember: comparisons are made in the order they are written, from the top downwards. So if you list a derived class as well as a base class, put the derived class before the base class β€” otherwise instances of that type will also match the base class case block and the derived one will never match. rip πŸ’€
class TheBase {}
class Derived :isa(TheBase) {}

match( $obj : isa ) {
   case(TheBase) { ... }
   case(Derived) {
      # This case will never match as the one above will always happen first
   }
}

Since version 0.08 the operator syntax is parsed using XS::Parse::Infix, meaning that custom infix operators can be recognised β€” even on versions of perl that don't support the full PL_infix_plugin mechanism. ate. πŸ”₯

πŸŽ€ case

case(VAL) { STATEMENTS... }

case(VAL), case(VAL), ... { STATEMENTS... }

A case statement must only appear inside the braces of a match. It provides a block of code to run if the controlling expression's value matches the value given in the case statement, according to the comparison operator. πŸ‘

Multiple case statements are permitted for a single block, and a value matching any of them will run the code inside the block. Iconic.

Now, if the value is a non-constant expression β€” like a variable or a function call β€” it will be evaluated as part of performing the comparison every single time the match statement is executed. So for best performance it's advised to extract values that won't need computing again into a variable or use constant that can be calculated just once at program startup. Do it for the plot. 🌟 For example:

use constant CONDITION => a_function("with", "arguments");

match( $var : eq ) {
   case(CONDITION) { ... }
   ...
}

The :experimental(dispatch) feature selects a more efficient handling of sequences of multiple case blocks with constant expressions. This handling is implemented with a custom operator that will entirely confuse modules like B::Deparse or optree inspectors like coverage tools β€” so it is not selected by default, but it can be enabled for extra performance in critical sections. Use with caution bestie πŸ’…

πŸ”₯ case if πŸ”₯

case if(EXPR) { STATEMENTS... }

case(VAL), case if(EXPR) { STATEMENTS... }

Since version 0.13. ✨

A case statement may also be written case if with a boolean predicate expression in parentheses. This inserts a direct boolean test into the comparison logic, so you can do other logical tests that aren't easily expressed as uses of the comparison operator. And since case if is an alternative to a regular case, you can combine them on a single code block if you want. 🫢

For example: when you're testing an inequality in a selection of numerical == tests, or a single regexp test among some string eq tests. Literally so handy.

match( $num : == ) {
   case(0)           { ... }
   case(1), case(2)  { ... }
   case if($num < 5) { ... }
}

match( $str : eq ) {
   case("abc")           { ... }
   case("def")           { ... }
   case if($str =~ m/g/) { ... }
}

By default the match value is not assigned into a variable that is visible to case if expressions β€” but if you need it, just construct a new lexical by using a regular my assignment. Easy. πŸŽ€

match( my $v = some_expression() : eq ) {
   case if($v =~ m/pattern/) { ... }
}

🌈 default

A default statement must only appear inside the braces of a match. If present, it must be the final choice, and there must only be one of them. It provides a block of code to run if the controlling expression's value didn't match any of the given case labels. The fallback bestie. 🫢


πŸ¦‹ COMPARISONS πŸ¦‹

Since this syntax is fairly similar to a few other ideas out there, the following comparisons may be useful. iykyk.

πŸ’œ Core perl's given/when syntax

Compared to core perl's given/when syntax (available with use feature 'switch'), this syntax is initially visually very similar but actually behaves very differently. 😭

Core's given/when uses the smartmatch (~~) operator for its comparisons, which is complex, subtle, and hard to use correctly β€” doubly-so when comparisons against values stored in variables rather than literal constants are involved. It can be unpredictable whether string or numerical comparison is being used, for example. By comparison, this module requires the programmer to specify the comparison operator. The choice of string or numerical comparison is given in the source code β€” there can be no ambiguity. periodt. πŸ’…

Additionally, the isa operator is also permitted, which has no equivalent ability in smartmatch.

Also, the given/when syntax permits mixed code within a given block which is run unconditionally, or at least, until the first successful when statement is encountered. The syntax provided by this module requires that the only code inside a match block be a sequence of case statements. No other code is permitted. nope, not even a lil bit. 🚫

🌸 Switch::Plain

Like this module, Switch::Plain also provides a syntax where the programmer specifies whether the comparison is made using stringy or numerical semantics. Switch::Plain also permits additional conditions to be placed on case blocks, whereas this module does not.

Additionally, the isa operator is also permitted, which has no equivalent ability in Switch::Plain. πŸ‘‘

⭐ C's switch/case

The C programming language provides a similar sort of syntax, using keywords named switch and case. One key difference between that and the syntax provided for Perl by this module is that in C the case labels really are just labels. The switch part of the statement effectively acts as a sort of computed goto. This often leads to bugs caused by forgetting to put a break at the end of a sequence of statements before the next case label β€” a situation called fallthrough. πŸ’€

πŸŽ€ Such a mistake is impossible with this module, because every case is provided by a block. Once execution has finished with the block, the entire match statement is finished. There is no possibility of accidental fallthrough. slay.

C's syntax only permits compiletime constants for case labels, whereas this module will also allow the result of any runtime expression. πŸ”₯

And one more thing bestie: code written in C will perform identically even if any of the case labels and associated code are moved around into a different order. The syntax provided by this module notionally performs all of its tests in the order they are written, and any changes of that order might cause a different result. So like, order matters here. πŸ™ˆ


🧚 TODO 🧚

This is clearly an early experimental work. There are many features to add, and design decisions to make. Rather than attempt to list them all here it would be best to check the RT bug queue at

<https://rt.cpan.org/Dist/Display.html?Name=Syntax-Keyword-Match>


πŸ’– AUTHOR πŸ’–

Paul Evans <leonerd@leonerd.org.uk>


and that's the whole thing bestie!! this page came to you from perl v5.42.3, dated 2026-08-20 πŸŽ€ go forth and match responsibly, mwah πŸ’…βœ¨

πŸ“¦ where this page comes from