README

_::Pattern::Match

This package provides the &choose function, which allows pattern-matching using Raku's signature destructuring as a more-powerful alternative to smartmatch's partial pattern matching. &choose takes a list of blocks and runs the first block with a signature that matches the current topic.

Because &choose uses signature destructuring, it supports binding to elements of the sub-signature. Thus, instead of this:

# Without Pattern::Match
for (:add(1, 5), :sub(9, 8), :mult(7, 7)) {
    when .key eq 'add' {
        say "{.value[0]} + {.value[1]} is {sum .value}" }
    when .key eq 'sub' {
        say "{.value[0]} - {.value[1]} is {[-] .value}" }
    when .key eq 'mult' {
        say "{.value[0]} Ɨ {.value[1]} is {[Ɨ] .value}" }
    default  { die "Unknown op: $_" }
}

You can write:

use _ <&choose>;
for (:add(1, 5), :sub(9, 8), :mult(7, 7)) {
    choose -> :$add  ($a, $b) { say "$a + $b is {$a+$b}" },
           -> :$sub  ($a, $b) { say "$a - $b is {$a-$b}" },
           -> :$mult ($a, $b) { say "$a Ɨ $b is {$aƗ$b}" },
           -> |cap            { die "Unknown op: " ~|cap }
}

&choose can work especially well with formal parameters and automatic signatures. Using those features, you could re-write the expression above as:

for (:add(1, 5), :sub(9, 8), :mult(7, 7)) {
    choose { say "$:add[0] + $:add[1] is {  [+] $add}"     },
           { say "$:sub[0] - $:sub[1] is {  [-] $sub[*]}"  },
           { say "$:mult[0] Ɨ $:mult[1] is {[Ɨ] $mult[*]}" },
           { die "Unknown op: " ~@_                        }
}

As with signatures, you can match against literals. Matches are evaluated from top to bottom (just as with given/when), so you can place more specific cases above more general ones:

my $today = Date.new: '2021-12-11';
say do given $today {
    choose -> $ (12 :$month, 25 :$day, |)          { "Merry Christmas!" },
           -> $ (12 :$month, :$day where 26..*, |) { "I hope you had a nice Christmas :)" },
           -> | { "Only {359 - .day-of-year} days 'till Christmas" }
    }

But if you put a more general type above specific type, it could make it impossible to match the more specific type. This is known as "shadowing"; &choose will throw an error if it detects a shadowed case:

my $today = Date.new: '2021-12-11';
say do given $today {
    choose -> | { "Only {359 - .day-of-year} days 'till Christmas" },
           -> $ (12 :$month, 25 :$day, |)          { "Merry Christmas!" },
           -> $ (12 :$month, :$day where 26..*, |) { "I hope you had a nice Christmas :)" },
    }

# THROWS with this message:
#   The pattern
#     ($ (Int :$month where { ... }, Int :$day where { ... }, |))
#   will never be matched because it is entirely shadowed by the prior pattern
#     (|)

&choose will also throw an error if the topic does not match any of the cases. If you want to allow non-matching input, you can set a default pattern with -> | (as in the prior example).

When you provide conditions for &choose, you are passing a list of Blocks to a function. This means that, unlike when blocks, the conditional blocks must use list syntax ā€“ that is, in the examples above, the trailing commas are required (except after the last block).

If you don't care for the look of the ,, you Raku allows you to separate list items with ; so long as it's clear that the semicolon isn't ending a statement. Here, this means using parenthesizes to call &choose; using this syntax, the first example could be written as:

for (:add(1, 5), :sub(9, 8), :mult(7, 7)) {
    choose( { say "$:add[0] + $:add[1] is {  [+] $add}"     };
            { say "$:sub[0] - $:sub[1] is {  [-] $sub[*]}"  };
            { say "$:mult[0] Ɨ $:mult[1] is {[Ɨ] $mult[*]}" };
            { die "Unknown op: " ~@_                        })
}

_ v0.0.1

A meta package for zero-dependency micro packages of under 70 lines of code

Authors

  • Daniel Sockwell

License

Artistic-2.0

Dependencies

Test Dependencies

Provides

  • Pattern::Match
  • Print::Dbg
  • Self::Recursion
  • Test::Doctest::Markdown
  • Test::Fluent
  • Text::Paragraphs
  • Text::Wrap
  • _

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.