Does pattern match in Raku have guard clause?

I've written two answers. This one answers the question in the title. (The other answers the "Is this right?" question in the body of your post.)

"Does pattern match in Raku have guard clause?"

Based on what little I know about Scala, some/most Scala pattern matching actually corresponds to using Raku signatures. (And guard clauses in that context are typically where clauses.)

Quoting Martin Odersky, Scala's creator, from The Point of Pattern Matching in Scala:

instead of just matching numbers, which is what switch statements do, you match what are essentially the creation forms of objects

Raku signatures cover several use cases (yay, puns). These include the Raku equivalent of the functional programming paradigmatic use in which one matches values' or functions' type signatures (cf Haskell) and the object oriented programming paradigmatic use in which one matches against nested data/objects and pulls out desired bits (cf Scala).

Consider this Raku code:

class body { has ( $.head, @.arms, @.legs ) } # Declare a class (object structure).

class person { has ( $.mom, $.body, $.age ) } # And another that includes first.

multi person's-age-and-legs                   # Declare a function that matches ...

  ( person                                    # ... a person ...

    ( :$age where * > 40,                     # ... whose age is over 40 ...

      :$body ( :@legs, *% ),                  # ... noting their body's legs ...

      *% ) )                                  # ... and ignoring other attributes.

  { say "$age {+@legs}" }                     # Display age and number of legs.

my $age = 42;                                 # Let's demo handy :$var syntax below.

person's-age-and-legs                         # Call function declared above ...

  person                                      # ... passing a person.

    .new:                                     # Explicitly construct ...

      :$age,                                  # ... a middle aged ...

      body => body.new:
        :head,
        :2arms,
        legs => <left middle right>           # ... three legged person.

# Displays "42 3"

Notice where there's a close equivalent to a Scala pattern matching guard clause in the above -- where * > 40. (This can be nicely bundled up into a subset type.)

We could define other multis that correspond to different cases, perhaps pulling out the "names" of the person's legs ('left', 'middle', etc.) if their mom's name matches a particular regex or whatever -- you hopefully get the picture.

A default case (multi) that doesn't bother to deconstruct the person could be:

multi person's-age-and-legs (|otherwise)
  { say "let's not deconstruct this person" }

(In the above we've prefixed a parameter in a signature with | to slurp up all remaining structure/arguments passed to a multi. Given that we do nothing with that slurped structure/data, we could have written just (|).)

Unfortunately, I don't think signature deconstruction is mentioned in the official docs. Someone could write a book about Raku signatures. (Literally. Which of course is a great way -- the only way, even -- to write stuff. My favorite article that unpacks a bit of the power of Raku signatures is Pattern Matching and Unpacking from 2013 by Moritz. Who has authored Raku books. Here's hoping.)

Scala's match/case and Raku's given/when seem simpler

Indeed.

As @jjmerelo points out in the comments, using signatures means there's a multi foo (...) { ...} for each and every case, which is much heavier syntactically than case ... => ....

In mitigation:

  • Simpler cases can just use given/when, just like you wrote in the body of your question;

  • Raku will presumably one day get non-experimental macros that can be used to implement a construct that looks much closer to Scala's match/case construct, eliding the repeated multi foo (...)s.


From what I see in this answer, that's not really an implementation of a guard pattern in the same sense Haskell has them. However, Perl 6 does have guards in the same sense Scala has: using default patterns combined with ifs. The Haskell to Perl 6 guide does have a section on guards. It hints at the use of where as guards; so that might answer your question.


I've written two answers. This one answers the "Is this right?" question in the body of your post. (The other answers the question in the title.)

"Is this right?"

No.

given $ch {
  when Int and * > 10 { say 65}
}

This code says 65 for any given integer, not just one over 10!

We can begin to see the underlying problem with this code:

.say for ('TrueA' and 'TrueB'),
         ('TrueB' and 'TrueA'),
         (Int and 42),
         (42 and Int)

displays:

TrueB
TrueA
(Int)
(Int)

The and construct boolean evaluates its left hand argument. If that evaluates to False, it returns it, otherwise it returns its right hand argument.

In the first line, 'TrueA' boolean evaluates to True so the first line returns the right hand argument 'TrueB'.

In the second line 'TrueB' evaluates to True so the and returns its right hand argument, in this case 'TrueA'.

But what happens in the third line? Well, Int is a type object. Type objects boolean evaluate to False! So the and duly returns its left hand argument which is Int (which the .say then displays as (Int)).

This is the root of the problem.

(To continue to the bitter end, the compiler evaluates the expression Int and * > 10; immediately returns the left hand side argument to and which is Int; then successfully matches that Int against whatever integer is given -- completely ignoring the code that looks like a guard clause (the and ... bit).)

If you were using such an expression as the condition of, say, an if statement, the Int would boolean evaluate to False and you'd get a false negative. Here you're using a when which uses .ACCEPTS which leads to a false positive (it is an integer but it's any integer, disregarding the supposed guard clause). This problem quite plausibly belongs on the traps page.

Brad++'s comment on your question shows a good solution:

when $_ ~~ Int and $_ > 10 { say 65 }

Another few possibilities:

when * ~~ Int and * > 10 { say 65 }
when my Int $ where $_ > 10 { say 65 }
when Int { proceed unless $_ > 10; { say 65 } }

See also my other answer.