The Pearls of Raku, Issue 5: the where clause

In this issue, I will be looking at the three use cases with the where clause. All the examples refer to the MAIN function, but the content is applicable to any other function.

In this issue, I will be looking at the three use cases with the where clause. All the examples refer to the MAIN function, but the content is applicable to any other function.

More than one parameter

If the function takes more than one parameter and you want to enforce some restrictions on the values, you do not have to create separate where clauses for each of the parameters.

For example, check if the two arguments of the MAIN function are within the given limits:

sub MAIN(Int $a where 10 < * <= 20, Int $b where * > 20) {
    say "OK: $a $b";
}

Here, each parameter has its own where clause. It is also possible to have a single where clause, especially when the values of the two parameters are connected.

sub MAIN(Int $a, Int $b where 10 < $a < $b > 20) {
    say "OK: $a $b";
}

Check the file

If you are passing the name of a file, you can check for its existence directly in the where clause! Look at the example:

sub MAIN(Str $filename where *.IO.f) {
    say "$filename exists";
}

If the files does not exist, the function (or the whole program in the case of the MAIN function) will not even be called.

Informative where

When you have the where clause in the MAIN function, and the arguments do not pass the checks, the compiler prints a default prompt listing the options to run the program. Consider the example:

sub MAIN(Int $x where $x < 10) {
    say "x = $x";
}

If you run the program with no arguments, you get the message:

$ raku where-int.raku 
Usage:
  where-int.raku <x>

This is good so far. But if you supply an integer which is incorrect, you get the same message:

$ raku where-int.raku 20
Usage:
  where-int.raku <x>

No information as to why it is not accepted.

One of the possible solutions is to add a special comment (a declarator block):

sub MAIN(Int $x where $x < 10 #= $x must be < 10
) {
    say "x = $x";
}

Now, the message is printed together with the usage message:

$ raku where-int.raku 20
Usage:
  where-int.raku <x> -- $x must be < 10

The second option is to use die directly in the where block:

sub MAIN(
    Int $x where {
        $x < 10 or die "Invalid \$x. Got $x but must be < 10."
    }) {
    say "x = $x";
}

Now, we are able to prepare an even more informative message:

$ raku where-print.raku 20
Invalid $x. Got 20 but must be < 10.
  in sub MAIN at where-print.raku line 1
  in block <unit> at where-print.raku line 1

* * *

Find the code of this issue on GitHub and browse for more Raku Pearls.

Leave a Reply

Your email address will not be published. Required fields are marked *