๐Ÿ“˜ Assignment meta-operator = in Perl 6

The design of the operators in Perl 6 is very consistent. For example, if you add a new operator to the language, Perl 6 will create a few more to keep the harmony. In this section, we will talk about the so-called meta-operators, the operators over other operators.

The assignment meta-operators (=) use the other operators to create the constructions like +=, ~=, etc. The action of the newly created operators is always equivalent to the verbose code.

If you type $a op= $b, then a compiler will execute the following action: $a = $a op $b.

That means that $x += 2 is equivalent to $x = $x + 2, and $str ~= ‘.’ to $str = $str ~ ‘.’.

Let us now create a new custom operator and see if the assignment meta-operator will be available for us. On purpose, I chose quite an outstanding-looking operator ^_^:

sub infix:<^_^>($a, $b) {
ย ย ย  $a ~ '_' ~ $b

First, check the simple usage of the new operator with two operands:

say 4 ^_^ 5; # 4_5

Then, let us try the meta-operator form of it: ^_^=:

my $x = 'file';ย 

$x ^_^= 101;
say $x; # file_101

๐Ÿ“˜ Reduction meta-operator [ ] in Perl 6

For any infix operator op the reduction form [op] also exists. The reduction operator takes a list, enrols its values, and inserts the operator between them.

Examine the example with the [*] reduction operator:

[*] 1..5

The form above is equivalent to the following line:

1 * 2 * 3 * 4 * 5

Reduction operators also will be automatically created for the user-defined operators. For example, create the operator that accumulates the sum of every pair of operands that it ever received. Notice the state variable, which works as it does in regular subs, keeping the value between the sub calls.

sub infix:<pairsum>($a, $b) {
ย ย ย  state $sum = 0;
ย ย ย  $sum += $a + $b;

say [pairsum] 1, 2, 3; # 9
say [pairsum] 1, 2, 3; # 27

To understand the result, letโ€™s add the debugging output to the operator body:

sub infix:<pairsum>($a, $b) {
ย ย ย  state $sum = 0;
ย ย ย  say "[$a + $b]";
ย ย ย  $sum += $a + $b;

Also note that the function returns the last calculated value; thatโ€™s why there is no need for an explicit return $sum statement.

So, the call of [pairsum] 1, 2, 3 prints the following lines:

[1 + 2]
[3 + 3]

It is important to realise that the second call receives the values 3 and 3, not 2 and 3 from the original sub call. This is because in the second call, the left operand will contain the previously calculated value, which is also 3 at that moment.

๐Ÿ“˜ Reverse meta-operator R in Perl 6

The prefix R forms the reverse operator for the infix operators, such as / or cmp. The reverse operator does the same as the original but changes the order of the operands.

If necessary, it also changes the operatorโ€™s associativity. This matters when you have more than two operands in a row. For example, in the code $a op $b $op $c the operators are calculated left to right; that is, first, the value of $a is evaluated. With the reverse operator, the value of $c will be calculated first in the same sequence $a Rop $b Rop $c.

say 2 R/ 10; # 5. Same as say 10 / 2

The reverse operators may be very useful together with the reduction operators. Here is an example of how you can reverse and join the values in one go:

say [R~] 'a'..'z'; # zyxwvutsrqponmlkjihgfedcba

๐Ÿ“˜ Cross meta-operator X in Perl 6

The cross meta-operator prefix, X, applies an operation to all the possible combinations of the elements of the operands that are treated in list context. The result of the cross-operation is also a list.

Here is an example that prints the coordinates for all the cells of a chess board:

say 'a'..'h' X~ 1..8;

๐Ÿ“˜ Hyper-operators in Perl 6

Hyper-operators modify regular operators in such a way that the operation is applied to all the element of a list operand. Both unary and binary operators may be written in the hyper-operator form. To create a hyper-operator, add a pair of >> and/or << to the operation sign.

Letโ€™s start with a simple unary operator !:

my @a = (True, False, True);
my @b = !<< @a;
say @b; # False True False

Another example, now with the postfix operator:

my @a = (1, 2, 3);
say @a; # [2 3 4]

In both examples, an operation was applied to each element of the given list. Keep in mind that you should avoid spaces around or inside the hyper-operator because the compiler may be confused otherwise.

my @a = ('a', 'b')>>.uc;
# You cannot type ('a', 'b') >>. uc

say @a; # [A B]

Hyper-operators use the angle brackets, and it is possible to make four different combinations and directions of them:

>>+>>  <<+<<  <<+>>  >>+<<

The direction of the double brackets changes the result of the hyper-operation. If both operands are of the same length, the symmetrical forms work the same:

my @a = (1, 2, 3) >>+<< (4, 5, 6);
say @a; # [5 7 9]

my @b = (1, 2, 3) <<+>> (4, 5, 6);
say @b; # [5 7 9]

When the lengths of the lists in operands are different, the direction of arrows indicates whether the compiler needs to extend the shortest operand so that there are enough elements to make the operation with all the elements of the longer list.

The simplest case is a combination of a list and a scalar:

say((1, 2, 3) >>+>> 1); # (2 3 4)

The scalar 1 will be repeated three times.

In the next example, we have two lists, but the second one is shorter.

my @a = (1, 2, 3, 4) >>+>> (1, -1);
say @a; # [2 1 4 3]

Now, the second list was repeated twice and the left array was in fact added to (1, -1, 1, -1). So as you see, the sharp end of the arrows points to the shorter operand.

If you reverse the order or the operands, you should also change the direction of the hyper-operator:

my @b = (1, -1) <<+<< (1, 2, 3, 4);
say @b; # [2 1 4 3]

If the list length is not known, use the form of the hyper-operator, where both arrows are pointing outside: <<+>>.

my @a = (1, -1) <<+>> (1, 2, 3, 4);
say @a; # [2 1 4 3]ย 

my @b = (1, 2, 3, 4) <<+>> (1, -1);
say @b; # [2 1 4 3]

It is not possible to use the operator >>+<< because it expects that both operands are of the same length, and if they are not, then a runtime error occurs:

Lists on either side of non-dwimmy hyperop of infix:<+> are not of the same length

Finally, it is possible to use non-ASCII characters and use the French quotes instead of the pair of angle brackets:

say((1,2) ยป+ยซ (3,4)); # (4 6)