Raku: a language where a value can have several values at once (Part 1)

If you did not get the idea from the title, here’s an example straight away:

my $x = 5;
say "OK" if $x == 3 | 5 | 7;

This program prints OK, as $x holds 5, which is one of the options listed in 3 | 5 | 7.

The construct 3 | 5 | 7 is called a junction — many values in a single value. And the result of a comparison with it is also a junction:

my $x = 5;
say (3 | 5 | 7).WHAT; # (Junction)
say ($x == 3 | 5 | 7).WHAT; # (Junction)

So, when we compare 5 in $x with 3 | 5 | 7, the result is any(False, True, False). It is enough for one of the three values to be true, so the whole comparison is also true. It’s that simple. You don’t have to worry about how Raku computes it: sequentially, in parallel using a multi-core processor or simultaneously in parallel universes; you just get the result.

To see the result in a simple Boolean form, convert a junction to a Boolean with so:

say so 5 == 3 | 5 | 7;
say so 4 == 3 | 5 | 7;

All four

It is now logically to extend the above idea. Indeed, in Raku, you have more options to refer to several values with a single word:

say so any(3, 5, 7) == 5;
say so all(2, 4, 6) %% 2;
say so one(1, 2, 3) > 2;
say so none(1, 2, 3) < 0;

All of the numbers 2, 4, 6 are divisible by 2. One of 1, 2, 3 is greater than 2, and none of 1, 2, 3 is negative.

Change the conditions and/or the numbers to confirm that Raku gets it right.

Beyond constants

Naturally, junctions work not only with integer constants. What a programmer may be more interested in is applying them to data structures.

my @ages = 19, 34, 27;

say so all(@ages) ≥ 18; 
say so any(@ages) > 30; 
say so none(@ages) < 0;

Here, the junctions run checks such as age limits for entering the club: all(@ages) ≥ 18 or sanity-checking the age values: none(@ages) < 0.

Such constructs are a natural choice for interval comparisons:

my @v = 3, 7, 5;

say so 0 ≤ all(@v) ≤ 10; # True
say so 0 ≤ all(@v) ≤ 6;  # False

Leaving aside the Unicode characters like vs. their ASCII equivalents <= etc., spelling out the rules to compare all the numbers in @v would require far more code than testing them with a junction function like all.

What is maybe even more appealing is that you can call a method right on all of the junction-containing values at once:

say (3 | 4 | 5).is-prime;

No need to loop over the items. A junction does the job. The above line of code prints any(True, False, True), which gives the three answers in one.

Similarly, it’s possible to pass a junction to a function that—as you might think—accepts a single scalar argument:

sub double($x) { $x × 2 }
say double(1 | 2 | 3);

All of the numbers will be multiplied by two: any(2, 4, 6).

And now let’s use junctions as restrictions in function signatures:

multi roll-die(Int $n where 1 | 3 | 5) {
    "odd $n"
}

multi roll-die(Int $n where 2 | 4 | 6) {
    "even $n"
}

say roll-die(6.rand.Int + 1) for ^10;

Here, we have two versions of the same function roll-die, each accepting only odd or even numbers. There is more than one way to test the numbers for that separation, one of them is to use junctions.

The loop runs 10 times and calls roll-die with random integer numbers between 1 and 6.

Leave a Reply

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

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code