Haskell-like pattern matching in Raku

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" }
}

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.


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.