Sun, 19 Oct 2008

Custom Operators

Permanent link


"Perl 5 to 6" Lesson 13 - Custom Operators


    multi sub postfix:<!>(Int $x) {
        my $factorial = 1;
        $factorial *= $_ for 2..$x;
        return $factorial;
    say 5!;                     # 120


Operators are functions with unusual names, and a few additional properties like precedence and associativity. Perl 6 usually follows the pattern term infix term, where term can be optionally preceded by prefix operators and followed by postfix or postcircumfix operators.

    1 + 1               infix
    +1                  prefix
    $x++                postfix
    <a b c>             circumfix
    @a[1]               postcircumfix

Operator names are not limited to "special" characters, they can contain anything except whitespace.

The long name of an operator is its type, followed by a colon and a string literal or list of the symbol or symbols, for example infix:<+> is the the operator in 1+2. Another example is postcircumfix:<[ ]>, which is the operator in @a[0].

With this knowledge you can already define new operators:

    multi sub prefix:<€> (Str $x) {
        2 *  $x;
    say €4;                         # 8


In an expression like $a + $b * $c the infix:<*> operator has tighter precedence than infix:<+>, which is why the expression is evaluated as $a + ($b * $c).

The precedence of a new operator can be specified in comparison to to existing operators:

    multi sub infix:<foo> is equiv(&infix:<+>) { ...  }
    mutli sub infix:<bar> is tighter(&infix:<+>) { ... }
    mutli sub infix:<baz> is looser(&infix:<+>) { ... }


Most infix operators take only two arguments. In an expression like 1 / 2 / 4 the associativity of the operator decides the order of evaluation. The infix:</> operator is left associative, so this expression is parsed as (1 / 2) / 4. for a right associative operator like infix:<**> (exponentiation) 2 ** 2 ** 4 is parsed as 2 ** (2 ** 4).

Perl 6 has more associativities: none forbids chaining of operators of the same precedence (for example 2 <=> 3 <=> 4 is forbidden), and infix:<,> has list associativity. 1, 2, 3 is translated to infix:<,>(1; 2; 3). Finally there's the chain associativity: $a < $b < $c translates to ($a < $b) && ($b < $c).

    multi sub infix:<foo> is tighter(&infix:<+>)
                          is assoc('left')
                          ($a, $b) {

Postcircumfix and Circumfix

Postcircumfix operators are method calls:

    class OrderedHash is Hash {
        method postcircumfix:<{ }>(Str $key) {

If you call that as $object{$stuff}, $stuff will be passed as an argument to the method, and $object is available as self.

Circumfix operators usually imply a different syntax (like in my @list = <a b c>;), and are thus implemented as macros:

    macro circumfix:«< >»($text) is parsed / <-[>]>+ / {
        return $text.comb(rx/\S+/);

The is parsed trait is followed by a regex that parses everything between the delimiters. If no such rule is given, it is parsed as normal Perl 6 code (which is usually not what you want if you introduce a new syntax). Str.comb searches for occurrences of a regex and returns a list of the text of all matches.

"Overload" existing operators

Most (if not all) existing operators are multi subs or methods, and can therefore be customized for new types. Adding a multi sub is the way of "overloading" operators.

    class MyStr { ... }
    multi sub infix:<~>(MyStr $this, Str $other) { ... }

This means that you can write objects that behave just like the built in "special" objects like Str, Int etc.


Allowing the user to declare new operators and "overload" existing ones makes user defined types just as powerful and useful as built in types. If the built in ones turn out to be insufficient, you can replace them with new ones that better fit your situation, without changing anything in the compiler.

It also removes the gap between using a language and modifying the language.


If you are interested in the technical background, ie how Perl 6 can implement such operator changes and other grammar changes, read

[/perl-5-to-6] Permanent link

comments / trackbacks