Thursday, July 5, 2007

Operating with operators

Pugs revision: r16707

Operators are fun. Using them is fun, and creating them is fun. We'll cover both in this post. It will be fairly short, but hopefully you'll find something interesting.

Note, I won't quite be covering Operator precedence. See S03 for details on that note.

Operators. Operators? Operators.

Junctions are a nice semantic addition to Perl 6; in short, a junction is a single value that represents multiple values. You essentially operate on top of these in parallel, and they can be quite useful.

Junctions are important enough to where the purpose of the operators |, & and ^ have been delegated to them, rather than being bit-twiddling operations as you'd think at first. With junctions, there are also the special functions any, one, all that are for their list counterparts:
pugs> any(1..5)
(1 | 2 | 3 | 4 | 5)
pugs> one(1..5)
(1 ^ 2 ^ 3 ^ 4 ^ 5)
pugs> all(1..5)
(1 & 2 & 3 & 4 & 5)
As you can see from the example, the meaning of the operators should be somewhat evident (now, at least.) How can we use them?
pugs> if 1 == any(1..10) {
....> say "works";
....> }
pugs> if 1 == all(1..10) {
....> say "works";
....> }
pugs> if 1 == one(1..10) {
....> say "works";
....> }
(Note that I am utterly terrible normally at thinking up code examples, if you guys can do any better I'll take all I can get.)

Naturally in the above example, your semantics and list you use will change, but you should get the idea that when you operate over junctions, you're operating over one aggregation of different things. An element in a junction is orthognoal to it's counterparts as order is not relevant.

Junctions are a useful addition. In general, you'll probably more directly deal with composing them from lists than directly per se, however, knowing the idea behind what every junctive operator does is good to know.

The range operator, .. is used to denote the set of values from one point to another. From 1 to 10, a to e, etc. etc... You can use them to represent values from positive infinity to negative infinity, a to z, 1 to whatever, ex:
0..*     # 0 to +infinity
*..0 # -infinity to 0
'a'..'e' # a to e
1..10 # one to two
Hopefully, most of these semantics should be obvious even without the comments.

Feed operators
Feeds are a lot like piping. You can take the return values from a function and 'feed' them to another quite seamlessly. For example, the results from map can be fed into your own custom function:
map { $_ % 2 == 0 },(1..10) ==> functor
The distinction between the two feed operators, <== and ==> are simply in which way your data flows.

Symbolic Unary operation
In the case of many operators, when used in an unary form, impose a context on the operation in general. For example, where ~ was normally string contatenation, when used in an unary form, i.e. ~$x you are forcing a string context on your variable. This definition is somewhat strict; an unary context for an operator can do a lot of things.

What're some of the things you can do with this? There is, for example, an 'from 0 to this' operator:
pugs> my $l = 10;
pugs> map { .say },^$l
You can also use this form to turn a list of numeric values into a list of string values:
pugs> ~1..10
("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
Or, you can also use the equal sign to iterate over an iterator. For example:
[altair@stormwind diveintoperl6]$ cat
say "enter something...";
my $l;
while $l = =$*IN {
say "you entered '"~$l~"'";
[altair@stormwind diveintoperl6]$ pugs
enter something...
you entered 'hello'
hi dive into perl6 crowd!
you entered 'hi dive into perl6 crowd!'
[altair@stormwind diveintoperl6]$
A list of all these rules can be found in S03.

Meta operator[s|ing]

Hyper operators
Hyper operators are a way of taking an operator, applying it to each element of it's list, and returning the resultant list. In this sense, it's somewhat similar to map.
A hyper operator is denoted by the operator surrounded by a >> and a <<. There are variations to this however, which we'll shortly see. Here's just an example of taking two lists, adding each of their elements to the corresponding one, and returning it:
pugs> (1,2,3,4,5) >>+<< (5,4,3,2,1)
(6, 6, 6, 6, 6)
What about an unary operator? Just use one of the symbols denoting a hyper operator, on the side which the arguments are expected:
pugs> ~<< (1..10)
("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
But what if the two lists are not of the same length? How would you simply increase every element in a list by one? At first it may seem as though you need some sort of 'hackery,' but Perl 6 will sufficiently upgrade it, however, you must 'point' the hyper operator towards the smaller list:
(1,2,3) >>+>> 1 # (2,3,4)
If you simply 'point' your hyper operator towards the short side, perl will take care of the rest. If you don't know which side will be smaller than the other (if at all,) simply make the hyper operators 'point outwards':
@a <<+>> @b
Reduction operators
Reduction operators are a meta operator that are used with a traditional infix operator, and they are used to 'reduce' a list of values into a single one. For example, here's a good one taken from freenode #perl6's channel title:
pugs> [~] <m oo se>
If you're familiar with Haskell, this is the same:
Prelude> foldr1 (++) ["m","oo","se"]
Cross operators
A cross operator is acheived by putting an infix operator inbetween two X's. It uses the infix operator and generates all permutations of the two lists given to it. Ex:
<austin brian> X~X <seipp stanford>
It's roughly that simple (note, not yet implemented.)

Creating operators
In Perl 6 you get a lot of the fun power of creating your own operators. What can they do? Well, just about whatever you want.

Operators in Perl 6 are typically defined as multi-subroutines, as you can have different types and contexts they're used in, i.e. you may need to add support for + to work with a class you define. Fairly typical stuff.

How do we define an operator? They are defined in the form of:
multi sub x:<y> (z) { ... }
  • x is one of infix, prefix, postfix, circumfix or postcircumfix. Essentially, these are the categories your operator fits into.
  • y is your operator. The characters can be any non-whitespace characters, unicode included.
  • z is/are your operands.
Actually, the part following the semicolon in the subroutine name (that's all operators are, special subroutines) can essentially be any form of a hash subscript, i.e. you could do <+> or {'+'}, etc. etc..

Unary operators can be defined either as prefix or postfix, with one operand (naturally.) Binary operators are defined as infix, and bracketing operators (for example, in HTML, the two parts of a comment: <!-- and -->) are defined with circumfix. postcircumfix is used where a postfix is expected.

This is best explained with a couple of good examples. For example, here's the reverse form of the xx operator:
pugs> multi sub infix:<~||~> (Int $n, Str $s) {
....> return $s xx $n;
....> };
pugs> 5 ~||~ "hi"
("hi", "hi", "hi", "hi", "hi")
What about a unary operator, that, say, uppercases the string that is prefixed to it?
pugs> multi sub postfix: (Str $s) {
....> $s.uc;
....> };
pugs> "arg"!!!
How about a 'plus or minus' operator?
pugs> multi sub prefix:<+/-> (Int $n) {
....> return +$n|-$n;
....> };
pugs> +/-5
(-5 | 5)
We could even define our own comment operator:
multi sub circumfix:{'~#','#'} ($s) { return ""; }
~# how incredibly fun it is to conceive operators #
say "hello world";
(Note, the [post]circumfix category for operators doesn't work yet (r16707), but hopefully it should soon.)

This was a short post but a fun one. In previous languages, operators seemed quite boring and trivial, but hopefully Perl 6's new features will show you how to have fun with such a simple primitive. :)