Haskell and Rust (and mabye other languages of which I am not aware) have a fature which they call "pattern matching". Here is an example in Haskell:
data Event = HoldKey Char | PressKey Char | Err String
someFunc = let
someEvent <- doSomeStuff
-- What follows is a case expression using pattern matching
thingINeed <- case someEvent of
HoldKey keySym -> process keySym
PressKey keySym -> process keySym
Err err -> exit err
in keepDoingStuff
The closest thing to this in Raku seems to be multimethods multifunctions (terminology fixed in an answer below, but multimethods would also work).
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
multi process(Hold (:$key)) { say $key; }
multi process(Press (:$key)) { say $key; }
multi process(Err (:$msg)) { say $msg; }
But this doesn't help if I want a "local" pattern matching expression (like the case
expression in the haskell snippet above). Something like this:
given Hold.new(:key<a>) {
when Hold (:$key) { say $key }
when Press (:$key) { say $key }
when Err (:$msg) { say $msg }
default { say "Unsupported" }
}
Which alas does not compile. So am I missing something or can Raku express this in some other way?
You are trying to use a Signature where no Signature is expected.
The Signature would more likely be part of the blocks, so it would look more like this:
given Hold.new(:key<a>) {
when Hold -> (:$key) { say $key }
when Press -> (:$key) { say $key }
when Err -> (:$msg) { say $msg }
default { say "Unsupported" }
}
That doesn't currently work as the blocks don't get any arguments.
It could be argued that this would be a useful feature to add.
Note that there is only two things that given
does.
proceed
and succeed
to finalize to.$_
to the value you are giving it.So that means that $_
is already set to the value you are smartmatching against.
given Hold.new(:key<a>) {
when Hold { say .key }
when Press { say .key }
when Err { say .msg }
default { say "Unsupported" }
}
I agree it'll be nice to have elegant syntax for this in Raku. But functionally speaking I think Raku's a bit closer to what you describe than you think it is.
The closest thing to this in Raku seems to be multimethods.
Raku does support multimethods. But what you showed were multifunctions.
I too think multifunctions are the closest thing Raku currently has to what you describe. But I also think they're closer than you currently think they are.
I want a "local" pattern matching expression
Multifunctions are local (lexically scoped).
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
{
match Hold.new: :key<a> ;
multi match (Hold (:$key)) { say $key }
multi match (Press (:$key)) { say $key }
multi match (Err (:$msg)) { say $msg }
multi match ($) { say "unsupported" }
}
# match; <--- would be a compile-time fail if not commented out
Yes, the above code is syntactically "off". Presuming RakuAST lands it'll presumably be particularly straight-forward to implement a nicer syntax. Perhaps:
match Hold.new: :key<a>
-> Hold (:$key) { say $key }
-> Press (:$key) { say $key }
-> Err (:$msg) { say $msg }
else { say "unsupported" }
where match
implements the multi
code I showed above, but:
Avoids need for an enclosing block;
Avoids need to explicitly write the function call;
Replaces multi match(...)
boilerplate with relatively terse ->
.
Makes else
clause mandatory (to avoid grammar ambiguity but has the nice side effect of enforcing explicit handling of a failure to otherwise match).
I noticed that you introduced OO for your example. (And called multifunctions multimethods.) Just to be clear, you don't need to use objects to do pattern matching using signatures in Raku.
The syntax you tried was actually extremely close. Here's what you want:
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
given Hold.new(:key<a>) {
when Hold { say .key }
when Press { say .key }
when Err { say .msg }
default { say "Unsupported" }
}
A few things to note: as the change in syntax shows, you are matching on the type of the Hold
, but you're not destructuring Hold
. Instead, you're taking advantage of the fact that given
blocks set the topic variable ($_
) inside the block.
Second (and it looks like you already realized this, but I'm adding it for future reference), it's important to note that given
blocks do not guarantee exhaustive pattern matching. You can simulate that guarantee with a default { die }
block (or, perhaps more semantically, by using the fatal stub operator: default {!!! 'unreachable'}
), but of course that's a runtime check rather than a compile-time one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With