Operators

Operators provide a simple syntax for manipulating values. A few characters take the place of a function call, or even several function calls. On the positive side this makes them incredibly convenient. On the negative side they’re also sometimes difficult to learn because they pack so much meaning into a small space. Many of the Perl 6 operators will be familiar, especially to Perl 5 programmers. The new operators either add new features to the language, or move Perl’s operator set toward a more consistent system.

Assignment and Binding

The = operator is for ordinary assignment. It creates a copy of the values on the righthand side and assigns them to the variables or data structures on the lefthand side:

$copy = $original;
@copies = @originals;

$copy and $original both have the same value, and @copies has a copy of every element in @originals.

The := operator is for binding assignment. Instead of copying the value from one variable or structure to the other, it creates an alias. An alias is an additional entry in the symbol table with a different name for the one container:

$a := $b;  # $a and $b are aliases
@c := @d;  # @c and @d are aliases

In this example, any change to $a also changes $b and vice versa, because they’re just two separate names for the same container. Binding assignment requires the same number of elements on both sides, so both of these would be an error:

# ($a, $b) := ($c);          # error
# ($a, $b) := ($c, $d, $e);  # error

The ::= operator is a variant of the binding operator that binds at compile time.

Arithmetic Operators

The arithmetic operators are addition (+), subtraction (-), multiplication (*), division (/), modulus (%), and exponentiation (**). Each has a corresponding assignment operator (+=, -=, *=, /=, %=, **=) that combines the arithmetic operation with assignment:

$a = 3 + 5;
$a += 5;     # $a = $a + 5

The unary arithmetic operators are the prefix and postfix autoincrement (++) and autodecrement (--) operators. The prefix operators modify their argument before it’s evaluated, and the postfix operators modify it afterward:

$a++;
$a--;
++$a;
--$a;

String Operators

The ~ operator concatenates strings. The corresponding ~= operator concatenates the righthand side of the assignment to the end of the string:

$line = "The quick brown " ~ $fox ~ jumps_over( ) ~ " the lazy " ~ $dog;
$line ~= "Belgium"; # appends to the string

The x operator replicates strings. It always returns a string no matter whether the left side of the operation is a single element or a list. The following example assigns the string “LintillaLintillaLintilla”:

$triplet = "Lintilla" x 3;

The corresponding x= operator replicates the original string and assigns it back to the original variable:

$twin = "Lintilla";
$twin x= 2;          # "LintillaLintilla"

List Operators

The xx operator replicates lists. It returns a list no matter whether it operates on a list of elements or a single element. The following example assigns a list of three elements to @array, each with a copy of the value “Lintilla”:

@array = "Lintilla" xx 3; # ("Lintilla", "Lintilla", "Lintilla")

The corresponding xx= operator creates a list that contains the specified number of copies of every element in the original array and assigns it back to the array variable:

@array = (4, 2);
@array xx= 2;              # now (4, 2, 4, 2)
@array = (@array, @array); # equivalent

The range operator .. returns a list of values from a starting point to an ending point:

@range = 3..7;   # 3,4,5,6,7

Ranges evaluate lazily, so a range containing an infinite value won’t try to calculate all the values before assigning the list. Instead, it returns a list generator that only generates elements as they’re requested.

@range = 3..Inf; # lazy

The . . . operator is equivalent to ..Inf:

@range = 3 . . . ;

Comparison

Each comparison operator has two forms, one for numeric comparisons and one for string comparisons. The comparison operators are greater-than (>, gt), less-than (<, lt), greater-than-or-equal (>=, ge), less-than-or-equal (<=, le), equal (= =, eq), and not-equal (!=, ne). The identity operator (=:=) tests whether the two arguments are aliases to the same object. Each returns a true value if the relation is true and a false value otherwise. The generic comparison operators (<=>, cmp) return 0 if the two arguments are equal, 1 if the first is greater, and -1 if the second is greater:

if ($age > 12) { . . . }

Comparison operators can also be chained. Chained comparisons evaluate each value in the chain only once:

if (24 < $age < 42) { . . . } # 24 < $age and $age < 42

Logical Operators

The binary logical operators test two values and return one value or the other depending on certain truth conditions. They’re also known as the short-circuit operators because the righthand side will never be evaluated if the overall truth value can be determined from the lefthand side. This makes them useful for conditionally assigning values or executing code.

The AND relation has the && operator and the low-precedence and operator. If the lefthand side evaluates as false, its value is returned. If the lefthand value is true, the righthand side is evaluated and its value is returned:

$splat = $whale && $petunia;
$splat = ($whale and $petunia);

The OR relation has the || operator and the low-precedence or operator. The lefthand value is returned if it is true; otherwise, the righthand value is evaluated and returned:

$splat = $whale || $petunia;
$splat = ($whale or $petunia);

A variant of the OR relation tests for definedness instead of truth. It uses the // operator and the low-precedence err operator. The lefthand value is returned if it is defined; otherwise, the righthand side is evaluated and its value returned:

$splat = $whale // $petunia;
$splat = ($whale err $petunia);

The XOR relation has the ^^ operator and the low-precedence xor operator. It returns the value of the true operand if any one operand is true, and a false value if both are true or neither is true. xor isn’t short-circuiting like the others, because it always has to evaluate both arguments to know if the relation is true:

$splat = $whale ^^ $petunia;
$splat = ($whale xor $petunia);

Perl 6 also has Boolean variants of the logical operators: ?& (AND), ?| (OR), and ?^ (XOR). These always return a true or false value.

$whale = 42;
$petunia = 24;
$value = $whale || $petunia   # $value is 42
$truth = $whale ?| $petunia   # $truth is 1

Context Forcing Operators

The context of an expression specifies the type of value it is expected to produce. An array expects to be assigned multiple values at the same time, so assignment to an array happens in list context. A scalar variable expects to be assigned a single value, so assignment to a scalar happens in scalar context. Perl expressions often adapt to their context, producing values that fit with what’s expected.

Contexts have proven to be valuable tools in Perl 5, so Perl 6 has a few more added. Void context still exists. Scalar context is subdivided into Boolean, integer, numeric, string, and object contexts. List context is subdivided into flattening-list context, nonflattening-list context, lazy list context, and hashlist context.

Void context

Expects no value.

Scalar context

Expects a single value. A composite value returns a reference to itself in scalar context.

Boolean context

Expects a true or false value. This includes the traditional definitions of truth—where 0, undef, and the empty string are false and all other values are true—and values flagged with the properties true or false.

Integer context

Expects an integer value. Strings are treated as numeric and floating-point numbers are truncated.

Numeric context

Expects a number, whether it’s an integer or floating-point, and whether it’s decimal, binary, octal, hex, or some other base.

String context

Expects a string value. It interprets any information passed to it as a string of characters.

Object context

Expects an object, or more specifically, a reference to an object. It may also expect an object of a particular type.

List context

Expects a collection of values. Any single value in list context is treated as a one-element list.

Flattening-list context

Expects a list. Flattens out arrays and hashes into their component parts.

Nonflattening-list context

Expects a list. Treats arrays, hashes, and other composite values as discrete entities.

Lazy list context

Expects a list, just like nonflattening-list context, but doesn’t require all the elements at once.

Hashlist context

Expects a list of pairs. A simple list in hashlist context pairs up alternating elements.

The unary context operators force a particular context when it wouldn’t otherwise be imposed. Generally, the default context is the right one, but at times you might want a little more control.

The unary ? operator and its low-precedence equivalent true force Boolean context. Assignment of a scalar to a scalar only imposes generic scalar context, so the value of $number is simply copied. With the ? operator, you can force Boolean context and assign the truth value of the variable instead of the numeric value:

$value = $number;
$truth = ?$number;

The unary ! operator and the low-precedence not also force Boolean context, but they negate the value at the same time. They’re often used in a Boolean context, where only the negating effect is visible.

$untruth = !$number;

The unary + operator forces numeric context, and - forces numeric context and negates the number at the same time:

$number = +$string;
$negnum = -$string;

The unary ~ operator forces string context:

$string = ~$number;

You can also create a scalar, list, or hashlist context with $( . . . ), @( . . . ), and %( . . . ).

Bitwise Operators

Perl 6 has two sets of bitwise operators, one for integers and one for strings. The integer bitwise operators combine the AND, OR, and XOR relation symbols with the general numeric symbol + (the unary numeric context operator). These are the binary +&, +|, and +^ and the unary +^ for bitwise negation (ones complement). The default integer type in Perl 6 is a signed int, so the results are equivalent to working with the use integer pragma turned on in Perl 5:

$number = 42 +& 18; # $number is 2
$number = 42 +| 18; # $number is 58
$number = 42 +^ 18; # $number is 56
$number = +^ 42;    # $number is -43

The numeric bitwise shift operators shift the value of the left operand by the number of bits in the right operand, either to the left (<<) or to the right (>>):

$number = 4 << 1; # $number is 8
$number = 4 >> 1; # $number is 2

The string bitwise operators combine the AND, OR, and XOR relation symbols with the general string symbol ~ (the same symbol as string concatenation and the unary string context operator). These are ~&, ~|, and ~^.

$string = 'jj' ~& 'gg'; # $string is 'bb'
$string = 'aa' ~| 'bb'; # $string is 'cc'
$string = "GG" ~^ "**"; # $string is 'mm'

Each of the binary bitwise operators has an assignment counterpart: +&=, +|=, +^=, <<=, >>=, ~&=, ~|=, and ~^=.

Conditional

The ternary ??:: operator evaluates either its second or third operand, depending on whether the first operand evaluates as true or false. It’s basically an if-then-else statement acting as an expression:

$form = ($heads =  = 2) ?? "Zaphod" :: "ape-descended lifeform";

Hyper Operators

The hyper operators are designed to work with lists. They’re simply modified versions of the standard scalar operators. Every operator has a hyper version, even user-defined operators. They have the same basic forms as their scalar counterparts, but are marked with the bracketing characters » and «,[5] or their plain-text equivalents >> and <<. For example, the hyper addition operator is >>+<<.

Hyper operators impose list context on their operands and distribute their operations across all the operands’ elements. Hyper addition takes each element from the first list and adds it to the corresponding element in the second list:

@sums = @first >>+<< @second;

The resulting array contains the sums of each pair of elements, as if each pair were added with the scalar operator:

@sums = ( (@first[0] + @second[0]), (@first[1] + @second[1]), etc . . . );

If one side of a hyper operation is a simple scalar, it is distributed across the list as if it were a list of identical elements:

@sums = @numbers >>+<< 5;

@sums ( (@numbers[0] + 5), (@numbers[1] + 5), etc . . .  );

Unary operators may also take a one-sided hyper on the side of their single operand:

@less = @numbers >>--;
@nums = +<< @strings;

Junctions

At the simplest level, junction operators are no more than AND, OR, XOR, and NOT for values instead of expressions. The binary junction operators are & (AND), | (OR), and ^ (XOR). There isn’t an operator for junctive NOT, but there is a function, as you’ll see shortly. So, while || is a logical operation on two expressions:

if ($value =  = 1) || ($value =  = 2) {  . . .  }

| is the same logical relation between two values:

if $value =  = 1 | 2 {  . . .  }

In fact, those two examples have exactly the same result: they return true when $value is 1 or 2 and false otherwise. In the common case, that’s all you’ll ever need to know.

But junctions are a good deal more powerful than that, once you learn their secrets. A junctive operation doesn’t return an ordinary single value, it returns a composite value containing all of its operands. This return value is a junction, and it can be used anywhere a junction operation is used:

$junc = 1 | 2;
if ($value =  = $junc) {  . . .  }

Here, the variable $junc is used in place of 1 | 2, and has exactly the same effect as the earlier example.

A junction is basically just an unordered set with a logical relation defined between its elements. Any operation on the junction is an operation on the entire set. Table 4-1 shows the way the four different types of junctions interact with other operators.

Table 4-1. Picture junctions

Function

Operator

Relation

Meaning

all

&

AND

Operation must be true for all values

any

|

OR

Operation must be true for at least one value

one

^

XOR

Operation must be true for exactly one value

none

NOT

Operation must be false for all values

The simplest possible example is the result of evaluating a junction in Boolean context. The operation on the set is just “is it true?” This operation on an all junction is true if all the values are true:

true( $a & $b )
true( all($a,$b) )

So, if both $a and $b are true, the result is true.

On an any junction, it’s true if any one value is true:

true( $a | $b )
true( any($a,$b) )

So, if $a or $b is true or if both are true, the result is true.

On a one junction, it’s true only if exactly one value is true:

true( $a ^ $b )
true( one($a,$b) )

So, if either $a or $b is true, the result is true. But, if $a and $b are both true or neither is true, the result is false.

On a none junction, it’s true only when none of the values are true—that is, when all the values are false:

true( none($a,$b) )

So, if $a and $b are both false, the result is true.

Ordinary arithmetic operators interact with junctions much like hyper operators on arrays. A junction distributes the operation across all of its elements:

$junc = any(1, 2);
$junc += 5; # $junc is now any(6, 7)

Junctions can be combined to produce compact and powerful logical comparisons. If you want to test that two sets have no intersection, you might do something like:

if all($a, $b) =  = none($c, $d) {  . . .  }

which tests that all of the elements of the first set are equal to none of the elements of the second set. Translated to ordinary logical operators that’s:

if ($a != $c) && ($a != $d) && ($b != $c) && ($b != $d) {  . . .  }

If you want to get back a flat list of values from a junction, use the .values method:

$junc = all(1, 2, 3);    # create a junction
$sums = $junc + 3;       # add 3
@set  = $sums.values( );  # (4, 5, 6)

The .dump method returns a string that shows the structure of a junction:

$string = $sums.dump( ); # "all(4,5,6)"

The .pick method selects one value from an any junction or a one junction that has exactly one value, and returns it as an ordinary scalar:

$junc = any(1, 2, 3);
$single = $junc.pick( );  # may be 1, 2, or 3

On an all junction, a none junction, or a one junction with more than one value, .pick returns undef. (With some levels of error strictness, it may raise an exception.)

Smart Match

The binary ~~ operator makes a smart match between its two terms. It returns a true value if the match is successful and a false value if the match fails.[6] The negated smart match operator !~ does the exact opposite: it returns true if the match fails and false if it is successful. The kind of match a smart match does is determined by the kind of arguments it matches. If the types of the two arguments can’t be determined at compile time, the kind of match is determined at run time. Smart match is usually a symmetric operator, so you can reverse A ~~ B to B ~~ A and it will have the same truth value.

Matching scalars

Any scalar value (or any code that results in a scalar value) matched against a string tests for string equality. The following match is true if $string has the value “Ford”:

$string ~~ "Ford"

Any scalar value matched against a numeric value tests for numeric equality. The following is true if $number has the numeric value 42, or the string value “42”:

$number ~~ 42

An expression that results in the value 42 is also true:

( (5 * 8) + 2 ) ~~ 42

Any scalar value matched against an undefined value checks for definedness. The following matches are true if $value is an undefined value and false if $value is any defined value:

$value ~~ undef
$value ~~ $undefined_value

Any scalar value matched against a rule (regex) does a pattern match. The following match is true if the sequence “towel” can be found anywhere within $string:

$string ~~ /towel/

Any scalar value matched against a substitution attempts that substitution on the value. This means the value has to be modifiable. The following match is true if the substitution succeeds on $string and false if it fails:

$string ~~ s/weapon/towel/

Any scalar value matched against a Boolean value simply takes the truth value of the Boolean. The following match will always be true, because the Boolean on the right is always true:[7]

$value ~~ (1 =  = 1)

The Boolean value on the right must be an actual Boolean: the result of a Boolean comparison or operation, the return value of a not or true function, or a value forced into Boolean context by ! or ?. The Boolean value also must be on the right; a Boolean on the left is treated as an ordinary scalar value.

Matching lists

Any scalar value matched against a list compares each element in sequence. The match is true if at least one element of the list would match in a simple expression-to-expression match. The following match is true if $value is the same as any of the three strings on the right:

$value ~~ ( "Zaphod", "Ford", "Trillian" )

This match is short-circuiting: it stops after the first successful match. It has the same truth value as a series of or-ed matches:

($value ~~ "Zaphod") or ($value ~~ "Ford") or ($value ~~ "Trillian")

A smart-matched list can contain any combination of elements: scalar values, rules, Boolean expressions, arrays, hashes, etc.:

$value ~~ ( "Zaphod", 5, /petunias/ )

A match of a list against another list sequentially compares each element in the first list to the corresponding element in the second list. The match is true if every element of the first list matches the corresponding element in the second list. The following match is true, because the two lists are identical:

( "Zaphod", "Ford", "Trillian" ) ~~ ( "Zaphod", "Ford", "Trillian" )

The two lists don’t have to be identical, as long as they’re the same length and their corresponding elements match:

( $zaphod, $ford, $trillian ) ~~ ( "Zaphod", /Ford/, /^T/ )

The list-to-list match is also short-circuiting. It stops after the first failed match. This has the same truth value as a series of single-element smart matches linked by and:

($zaphod ~~ "Zaphod") and ($ford ~~ /Ford/) and ($trillian ~~ /^T/)

Matching arrays

A nonnumeric expression matched against an array sequentially searches for that value in the array. The match is true if the value is found. If @array contains the values “Zaphod”, “Ford”, and “Trillian”, the following match is true when $value matches any of those three strings:

$value ~~ @array

An integer value matched against an array tests the truth of the value at that numeric index. The following match is true if the element @array[2] exists and has a true value:

2 ~~ @array

An integer value matched against an array reference also does an index lookup:

2 ~~ [ "Zaphod", "Ford", "Trillian" ]

This match is true, because the third element of the array reference is a true value.

An array matches just like a list of scalar values if it’s flattened with the * operator (See Section 4.2.13 later in this chapter). So, the following example searches the array for an element with the value 2, instead of doing an index lookup:

2 ~~ *@array

An array matched against a rule does a pattern match against every element of the array. The match is true if any element matches the rule. If “Trillian”, “Milliways”, or “million” is an element of @array, the following match is true, no matter what the other elements are:

@array ~~ /illi/

A match of an array against an array sequentially compares each element in the first array to the corresponding element in the second array:

@humans ~~ @vogons

This match is true if the two arrays are the same length and @humans[0] matches @vogons[0], @humans[1] matches @vogons[1], etc.

Matching hashes

A hash matched against any scalar value tests the truth value of the hash entry with that key:

$key ~~ %hash

This match is true if the element %hash{$key} exists and has a true value.

A hash matched against a rule does a pattern match on the hash keys:

%hash ~~ /blue/

This match is true if at least one key in %hash matches the string “blue”.

A hash matched against a hash checks for intersection between the keys of the two hashes:

%vogons ~~ %humans

So, this match is true if at least one key from %vogons is also a key of %humans. If you want to see that two hashes have exactly the same keys, match their lists of keys:

%vogons.keys.sort ~~ %humans.keys.sort

A hash matched against an array checks a slice of a hash to see if its values are true. The match is true if any element of the array is a key in the hash and the hash value for that key is true:

%hash ~~ @array

If @array has one element “blue” and %hash has a corresponding key “blue”, the match is true if %hash{'blue'} has a true value, but false if %hash{'blue'} has a false value.

Matching junctions

An expression matched against an any junction is a recursive disjunction. The match is true if at least one of the elements of the list would match in a simple expression-to-expression match:

$value ~~ any("Zaphod", "Ford", "Trillian")

This example matches if $value is the same as any of the three strings on the right. The effect of this comparison is the same as a simple comparison to a list, except that it isn’t guaranteed to compare in any particular order.

A smart match of an all junction is only true when the expression matches every value in the junction:

/illi/ ~~ all("Gillian", "million", "Trillian")  # match succeeds
/illi/ ~~ all("Zaphod", "Ford", "Trillian")      # match fails

A smart match of a one junction is only true when the expression matches exactly one value in the junction:

/illi/ ~~ one("Zaphod", "Ford", "Trillian")      # match succeeds
/illi/ ~~ one("Gillian", "million", "Trillian")  # match fails

A smart match of a none junction is true when it doesn’t match any values in the junction:

/illi/ ~~ none("Zaphod", "Ford", "Marvin")    # match succeeds
/illi/ ~~ none("Zaphod", "Ford", "Trillian")  # match fails

An any junction matched against another any junction is a recursive disjunction of every value in the first junction to every value in the second junction. The match is true if at least one value of the first junction matches at least one value in the second junction:

any("Ford", "Trillian") ~~ any("Trillian", "Arthur")

This match is true, because “Trillian” is in both junctions.

Matching objects

An object matched against a class name is true if the object belongs to that class or inherits from that class. It’s essentially the same as calling the .isa method on the object:

$ship ~~ Vogon::Constructor   # $ship.isa(Vogon::Constructor)

Matching subroutines

Any expression matched against a subroutine tests the return value of the subroutine. If the subroutine takes no arguments, it is treated as a simple Boolean:

$value ~~ my_true

If the subroutine has a one argument signature and it is compatible with the variable type of the expression, the subroutine is called with the expression as its argument:

$value ~~ &value_test   # value_test($value)
@array ~~ &array_test   # array_test(@array)
%hash  ~~ &hash_test    # hash_test(%hash)

The return value of the subroutine determines the truth of the match.

A block matches as an anonymous subroutine. The return value of the block determines the truth of the match. It’s treated as a simple Boolean if it takes no arguments, or passed the value on the left side if it uses $_ or placeholder variables inside the block (see Section 5.2.7 in Chapter 5).

$value ~~ { $_ + 5; }    # $_ is $value
%hash  ~~ { $_.keys; }   # $_ is \%hash
@array ~~ { @^a.elems; } # @^a is @array

Referencing (or Not)

The unary \ operator returns a reference to its operand. The referencing operator isn’t needed very often, since scalar context automatically generates references to arrays, hashes, and functions, but it is still needed in flattening contexts and other contexts that don’t auto-reference:

@array_of_refs = ( \@a, \@b, \@c );

Ordinarily, an array assigned a list of arrays would flatten the elements of all the arrays into a single array. With the referencing operator, @array_of_refs is assigned a list of three arrayrefs.

The unary * operator (known as the splat operator) flattens a list in a context where it would usually be taken as a reference. On an rvalue, * causes the array to be treated as a simple list:

@combo = (\@array, \%hash);
@a := @combo; # @a is @combo
(@b, %c) := *@combo; # @b is @array, %c is %hash

Since the @combo array contains an arrayref and a hashref, an ordinary binding assignment of @combo to @a treats @combo as a single element and binds it to @a. With the flattening operator, the @combo array is treated as a simple list, so each of its elements are bound to a separate element on the lefthand side. @b is bound to the original @array and %c is bound to the original %hash.

On an lvalue, * tells the array to slurp all available arguments. An ordinary binding of two arrays to two arrays simply binds the first element on the righthand side to the first element on the lefthand side, and the second to the second. So, @a is bound to @c, and @b is bound to @d:

(@a, @b) := (@c, @d); # @a is @c, @b is @d

With the * operator, the first element on the lefthand side flattens all the elements on the righthand side into a list before the binding assignment. So, @a contains all the elements from @c and @d:

*@a := (@c, @d); # @a contains @c and @d

One common use for * is in defining subroutine and method signatures, as you will see in Section 5.2.3 in Chapter 5.

Zip Operator

The ¦ operator takes two or more lists (arrays, hash keys, etc.) and returns a single list with alternating elements from each of the original lists. This allows loops and other iterative structures to iterate through the elements of several lists at the same time:

@a = (1, 2, 3);
@b = (4, 5, 6);

@c = @a ¬¦ @b; # @c is (1, 4, 2, 5, 3, 6)

There is no equivalent ASCII operator for the zip operator, but the zip function is much more fully featured than the operator. It is described in Section 4.3.2.3 later in this chapter.



[5] These are the Unicode RIGHT POINTING GUILLEMET (U+00BB) and LEFT POINTING GUILLEMET (U+00AB) characters.

[6] This is an oversimplification. Some matches return a more complex value, but in Boolean context it will always evaluate as true for a successful match, and false for a failed match.

[7] At the moment this relation won’t seem particularly useful. It makes much more sense when you realize that the switch statement duplicates all the smart match relations. More on that in Section 4.3.1.3 later in this chapter.

Get Perl 6 and Parrot Essentials, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.