O'Reilly logo

Perl 6 Essentials by Leopold Tötsch, Dan Sugalski, Allison Randal

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Operators

Operators provide a simple syntax for manipulating values. Many of the Perl 6 operators will be familiar, especially to Perl programmers.

Assignment and Binding

The = operator is for ordinary assignment. It creates a copy of the values on the right-hand side and assigns them to the variables or data structures on the left-hand 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, 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 binary 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 right-hand side of the assignment to the end of the string:

$line = "The quick brown " ~ $fox ~ jumps_over(  ) ~ " the lazy " ~ $dog;
$line ~= "Belgium"; # adds 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"

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 the value “Lintilla”:

@array = "Lintilla" xx 3;

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 xx= 2; # twice as many elements
@array = (@array, @array); # equivalent

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), equality (= =, eq), and inequality (!=, ne). Each returns a true value if the relation is true and a false value otherwise. The generic comparison operator (<=>, cmp) returns 0 if the two arguments are equal, 1 if the first is greater, and -1 if the second is greater.

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 right-hand side will never be evaluated if the overall truth value can be determined from the left-hand 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 left-hand side is false, its value is returned. If the left-hand value is true, the right-hand 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 left-hand value is returned if it is true, otherwise the right-hand 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 left-hand value is returned if it is defined, otherwise the right-hand 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.

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. Aside from flattening list context and hashlist context, which we mentioned earlier, list context is further subdivided into non-flattening list context and lazy list context.

Void context

Expects no value.

Scalar context

Expects a single value. Composite values are automatically referenced 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.

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.

Integer context

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

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.

List context

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

Non-flattening list context

Expects a list of objects. It treats arrays, hashes, and other composite values as discrete entities.

Flattening list context

Expects a list, but flattens out arrays and hashes into their component parts.

Hashlist context

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

Lazy list context

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

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

The unary ? operator and the low-precedence 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 negative numeric context:

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

The unary ~ operator forces string context:

$string = ~$number;

Bitwise Operators

Perl 6 has two sets of bitwise operators, one for integers and one for strings. The integer bitwise operators are +& , +|, and +^. Notice the combination of the AND, OR, and XOR relation symbols with the general numeric symbol + (the unary numeric context operator). There are also the numeric bitwise shift operators << and >>.

The string bitwise operators are ~& , ~|, and ~^. These 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).

Each of the 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";

Vector Operators

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

So, the vectorized addition operator is >>+<<. Vector operators impose list context on their operands and distribute their operations across all the operands’ elements. Vector 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 vector 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... );

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).[9]

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. In scalar context, 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. 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 $a and $b are both 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 vector 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.[10]

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.[11] 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 runtime. In all but two cases smart match is 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 does 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:[12]

$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 the 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 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 is the same as 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.[13]

So, the following example searches the array for an element with the value 2, instead of doing a index lookup:

2 ~~ *@array

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

@array = ( "Zaphod", "Ford", "Trillian" );
@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 ~~ /bl/

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

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 ~~ %humans.keys

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 (`0', an empty string or undef).

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)

An object calls a method it’s matched against. The match is true if the method returns a true value:

$ship ~~ .engage   # $ship.engage

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 the same variable type as the expression, the subroutine is called with the expression as its argument. The return value of the subroutine determines the truth of the match:

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

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 );

The unary * operator (that’s 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 left-hand 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 right-hand side to the first element on the left-hand side, and the second to the second:

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

So, @a is bound to @c, and @b is bound to @d. With the * operator, the first element on the left-hand side sucks up all the elements on the right-hand side, so @a contains all the elements from @c and @d.

One common use for * is in defining subroutine and method signatures, as you will see in Section 4.4 later in this chapter.

Zip Operator

The ¦ operator takes two 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.



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

[9] There isn’t an operator for junctive NOT, but there is a function, as you’ll see shortly.

[10] With some levels of error strictness, it may raise an exception.

[11] 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.

[12] 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.

[13] See Section 4.2.12 later in the chapter.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required