Search code examples
haskellfunctional-programmingpattern-matchingraku

Haskell-like pattern matching in Raku


Haskell and Rust (and maybe other languages of which I am not aware) have a feature 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 multi routines (both functions and methods).

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?


Solution

  • 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.

    1. Act like a block for proceed and succeed to finalize to.
    2. Set $_ 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" }
    }