NAME

Synopsis_03 - Summary of Perl 6 Operators

AUTHOR

Luke Palmer <luke@luqui.org>

VERSION

Maintainer: Larry Wall <larry@wall.org>
Date: 8 Mar 2004
Last Modified: 23 Nov 2005
Number: 3
Version: 10

Operator renaming

Several operators have been given new names to increase clarity and better Huffman-code the language:

  • -> becomes ., like the rest of the world uses.

  • The string concatenation . becomes ~. Think of it as "stitching" the two ends of its arguments together.

  • Unary ~ now imposes a string context on its argument, and + imposes a numeric context (as opposed to being a no-op in Perl 5). Along the same lines, ? imposes a boolean context, and * imposes a list context.

  • Bitwise operators get a data type prefix: +, ~, or ?. For example, | becomes either +| or ~| or ?|, depending on whether the operands are to be treated as numbers, strings, or boolean values. Left shift << becomes +< , and correspondingly with right shift. Unary ~ becomes either +^ or ~^ or ?^, since a bitwise NOT is like an exclusive-or against solid ones. Note that ?^ is functionally identical to !. ?| differs from || in that ?| always returns a standard boolean value (either 1 or 0), whereas || return the actual value of the first of its arguments that is true.

  • x splits into two operators: x (which concatenates repetitions of a string to produce a single string), and xx (which creates a list of repetitions of a list or scalar).

  • Trinary ? : becomes ?? !!.

  • qw{ ... } gets a synonym: < ... >, and an interpolating variant, «...». For those still living without the blessings of Unicode, that can also be written: << ... >>.

  • The scalar comma , now constructs a List reference of its operands. You have to use a [-1] subscript to get the last one.

  • The backslash operator still produces a reference, but when applied to a parenthesized list, produces a List object (an arglist) instead of a list of refs. (According to the previous item, in scalar context the backslash is redundant, but is good documentation anyway.)

  • The old scalar .. flipflop operator is now done with till operator. (.. now always produces a Range object even in scalar context.) The till operator may take a caret on either end to exclude either the beginning or ending.

New operators

  • Binary // is just like ||, except that it tests its left side for definedness instead of truth. There is a low-precedence form, too: err.

  • Binary === tests type and value correspondence: for two value types, tests whether they are the same value (eg. 1 === 1); for two reference types, checks whether they have the same identity value. For reference types that do not define an identity, the reference itself is used (eg. it is not true that [1,2] === [1,2], but it is true that \@a === \@a). Any reference type may pretend to be a value type by defining a .id method. (It may also overload infix:<===> for more efficient comparison of any two objects of that type, but it had better return the same result as if the two identity values had been generated and compared.) Two values are never equivalent unless they are of exactly the same type. By contrast, eq always coerces to string, while == always coerces to numeric. In fact, $a eq $b really means "~$a === ~$b" and $a == $b means "+$a === +$b.

    Note also that, while string hashes use eq semantics by default, object hashes use === semantics.

  • Binary => is no longer just a "fancy comma." It now constructs a Pair object that can, among other things, be used to pass named arguments to functions. It provides scalar context to both sides. Its precedence is now equivalent to assignment, and it is right associative.

  • ^^ is the high-precedence version of xor.

  • =~ becomes the "smart match" operator ~~, with a whole new set of semantics. Anywhere you used =~ before you now use ~~, but ~~ is much more general now. See "Synopsis 4" for details.

  • Unary . calls its single argument (which must be a method, or an dereferencer for a hash or array) on $_.

  • The .. range operator has variants with ^ on either end to indicate exclusion of that endpoint from the range. It always produces a Range object. Range objects are lazy iterators, and can be interrogated for their current .min and .max values (which change as they are iterated). Ranges are not autoreversing: 2..1 is always a null range, as is 1^..^2. However, smart matching against a Range object smartmatches the endpoints in the domain of the object being matched, so 1.5 ~~ 1^..^2 is true. (But 2.1 ~~ 1..2 is false.)

  • The unary ^ operator generates a range from 0 up to one less than its argument. so ^4 is short for 0..^4 or 0..3.

    for ^4 { say $_ } # 0, 1, 2, 3
  • ... is a unary postfix operator that constructs a semi-infinite (and lazily evaluated) list, starting at the value of its single argument.

  • However, ... as a term is the "yada, yada, yada" operator, which is used as the body in function prototypes. It complains bitterly if it is ever executed. Actually, it calls fail. Variant ??? calls warn, and !!! calls die.

  • In addition, to the ordinary . method invocation, there are variants .*, .?, and .+ to control how multiple parent methods of the same name are handled. The .= operator does inplace modification of the object on the left.

  • Unary = reads lines from a filehandle or filename, or in general iterates an iterator.

Hyper operators

The Unicode characters » (\x[BB]) and « (\x[AB]) and their ASCII digraphs >> and << are used to denote "list operations", which operate on each element of two lists (or arrays) and return a list (or array) of the results. Spaces are not allowed on the "pointy" end of each "hyper", but are allowed on the blunt end. For example:

(1,1,2,3,5) »+« (1,2,3,5,8);  # (2,3,5,8,13)

If one argument is insufficiently dimensioned, Perl "upgrades" it:

(3,8,2,9,3,8) >>-<< 1;          # (2,7,1,8,2,7)

When using a unary operator, only put it on the operand's side:

@negatives = -« @positives;

@positions »++;            # Increment all positions

@objects ».run();
("f","oo","bar")».chars;   # (1,2,3)

Note that method calls are really postfix operators, not infix, so you shouldn't put a « after the dot.

Hyper operators are defined recursively on array references, so:

-« [[1, 2], 3]               #    [-«[1, 2], -«3]
                             # == [[-1, -2], -3]
[[1, 2], 3] »+« [4, [5, 6]]  #    [[1,2] »+« 4, 3 »+« [5, 6]]
                             # == [[5, 6], [8, 9]]

Reduction operators

The other metaoperator in Perl 6 is the reduction operator. Any binary operator can be surrounded by square brackets in term position to create a list operator that reduces using that operation:

[+] 1, 2, 3;      # 1 + 2 + 3 = 6
my @a = (5,6);
[*] @a;           # 5 * 6 = 30

The reduction associates the same way as the operator used:

[-] 4, 3, 2;      # 4-3-2 = (4-3)-2 = -1
[**] 4, 3, 2;     # 4**3**2 = 4**(3**2) = 262144

For list-associating operators (like <), all arguments are taken together, just as if you had written it out explicitly:

[<] 1, 3, 5;      # 1 < 3 < 5

If no arguments are given, the operator calls fail (returning undef, or throwing an exception if use fatal is in effect). If exactly one argument is given, it is returned by default. However, this default doesn't make sense for an operator like < that doesn't return the same type as it takes, so these kinds of operators overload the single-argument case to return something more meaningful. All the comparison operators return truth in this case.

This metaoperator can also be used on the semicolon second-dimension separator:

[[;] 1,2,3]   # equivalent to [1;2;3]

Junctive operators

|, &, and ^ are no longer bitwise operators (see "Operator Renaming") but now serve a much higher cause: they are now the junction constructors.

A junction is a single value that is equivalent to multiple values. They thread through operations, returning another junction representing the result:

(1|2|3) + 4;                            # 5|6|7
(1|2) + (3&4);                          # (4|5) & (5|6)

Note how when two junctions are applied through an operator, the result is a junction representing the operator applied to each combination of values.

Junctions come with the functional variants any, all, one, and none.

This opens doors for constructions like:

unless $roll == any(1..6) { print "Invalid roll" }

if $roll == 1|2|3 { print "Low roll" }

Junctions work through subscripting:

print if @foo[any(1,2,3)]

Junctions are specifically unordered. So if you say

for all(@foo) {...}

it indicates to the compiler that there is no coupling between loop iterations and they can be run in any order or even in parallel.

Chained comparisons

Perl 6 supports the natural extension to the comparison operators, allowing multiple operands.

if 3 < $roll <= 6              { print "High roll" }

if 1 <= $roll1 == $roll2 <= 6  { print "Doubles!" }

Note: any operator beginning with < must have whitespace in front of it, or it will be interpreted as a hash subscript instead.

Binding

A new form of assignment is present in Perl 6, called "binding," used in place of typeglob assignment. It is performed with the := operator. Instead of replacing the value in a container like normal assignment, it replaces the container itself. For instance:

my $x = 'Just Another';
my $y := $x;
$y = 'Perl Hacker';

After this, both $x and $y contain the string "Perl Hacker," since they are really just two different names for the same variable.

There is another variant, spelled ::=, that does the same thing at compile time.

There is also an identity test, =:=, which tests whether two names are bound to the same underlying variable. $x =:= $y would return true in the above example.

List flattening

Since typeglobs are being removed, unary * may now serve as a lazy list flattening operator. It is used to "flatten" an Array (or List) into the List being constructed for the current call, usually to allow the array's contents to be used as the arguments of a subroutine call. Note that those arguments still must comply with the subroutine's signature, but the presence of * defers that test until run time for that argument (and for any subsequent arguments):

my @args = (\@foo, @bar);
push *@args;

is equivalent to:

push @foo, @bar;

as is this:

my $args = \(@foo, @bar);	 # construct List object
push *$args;

In list context, a scalar reference to an array does not flatten. Hence

$bar = @bar;
push @foo, $bar;

merely pushes a single scalar reference onto @foo. You can explicitly flatten it in any of these ways:

push @foo, *$bar;
push @foo, @$bar;
push @foo, $bar[];

(The * in list context doesn't change the call semantics as is does in scalar context.) Note that these three forms also allow you to specify list context on assignment:

*$bar = (1,2,3);
@$bar = (1,2,3);
$bar[] = (1,2,3);

The last is particularly useful at the end of a long name naming an array attribute:

$foo.bar.baz.bletch.whatever.attr[] = (1,2,3)

The empty [] and .[] postfix operators are interpreted as zero-dimensional slices returning the entire array, not null slices returning no elements. Likewise for {} and .{} on hashes, not to mention the <>, .<>, «», and .«» constant and interpolating slice subscripting forms.

The * operator flattens lazily. To get an immediate flattening like Perl 5 does, use unary ** instead. You may use either of them on a scalar iterator to force it to iterate.

Piping operators

The new operators ==> and <== are akin to UNIX pipes, but work with functions that accept and return lists. For example,

@result = map { floor($^x / 2) }
            grep { /^ \d+ $/ }
              @data;

Can also now be written:

@data ==> grep { /^ \d+ $/ }
      ==> map { floor($^x / 2) }
      ==> @result;

or:

@result <== map { floor($^x / 2) }
        <== grep { /^ \d+ $/ }
        <== @data;

Either form more clearly indicates the flow of data. See "Synopsis 6" for more of the (less-than-obvious) details on these two operators.

Invocant marker

An appended : marks the invocant when using the indirect-object syntax for Perl 6 method calls. The following two statements are equivalent:

$hacker.feed('Pizza and cola');
feed $hacker: 'Pizza and cola';

A colon may also be used on an ordinary method call to indicate that it should be parsed as a list operator:

$hacker.feed: 'Pizza and cola';

zip

In order to support parallel iteration over multiple arrays, Perl 6 has a zip function that builds tuples of the elements of two or more arrays.

for zip(@names; @codes) -> [$name, $zip] {
    print "Name: $name;   Zip code: $zip\n";
}

zip has an infix synonym, the Unicode operator ¥.

To read arrays in parallel like zip but just sequence the values rather than generating tuples, use each instead of zip.

for each(@names; @codes) -> $name, $zip {
    print "Name: $name;   Zip code: $zip\n";
}

The each function reads to the end of the longest list, not counting lists that are known to be infinite such as 0..Inf. Missing values are replaced with undef. In contrast, use roundrobin if you just wish to skip missing entries:

for roundrobin(@queue1; @queue2; @queue3) -> $next {
    ...
}

To read arrays serially rather than in parallel, use cat(@x;@y).

Minimal whitespace DWIMmery

Whitespace is no longer allowed before the opening bracket of an array or hash accessor. That is:

%monsters{'cookie'} = Monster.new;  # Valid Perl 6
%people  {'john'}   = Person.new;   # Not valid Perl 6

One of the several useful side-effects of this restriction is that parentheses are no longer required around the condition of control constructs:

if $value eq $target {
    print "Bullseye!";
}
while 0 < $i { $i++ }

It is, however, still possible to align accessors by explicitly using the . operator:

%monsters.{'cookie'} = Monster.new;
%people  .{'john'}   = Person .new;
%cats    .{'fluffy'} = Cat    .new;

Precedence

Perl 6 has 22 precedence levels (fewer than Perl 5):

terms		42 "eek" $x /abc/ (1+2) a(1) :by(2) .meth listop
method postfix	. .+ .? .* .() .[] .{} .«» .=
autoincrement	++ --
exponentiation	**
symbolic unary	! + - ~ ? * ** +^ ~^ ?^ \ ^
multiplicative	* / % x xx +& +< +> ~& ~< ~>
additive		+ - ~ +| +^ ~| ~^
junctive and (all)	&
junctive or (any)	| ^
named unary		rand sleep abs etc.
nonchaining binary	but does cmp <=> .. ^.. ..^ ^..^ till
chaining binary	!= == < <= > >= ~~ !~ eq ne lt le gt ge =:= ===
tight and		&&
tight or		|| ^^ //
ternary		?? !!
assignment		= := ::= += -= **= xx= etc. (and also =>)
list item separator	, ¥
list op (rightward)	<== print push any all true not etc.
pipe forward	==>
loose and		and
loose or		or xor err
expr terminator	; {} as control block, statement modifiers