Perl 6 small stuff #3: Custom operators and overloading

After a few years away from programming, I’m trying to get up to speed again by learning Perl 6. This series is meant to be sort of a progress report, showcasing not only what I’ve learnt but also all of my misunderstandings and errors.

Some would say that this post would be better if titled “You didn’t, did you?”, because custom operators and operator overloading is bound to get you in trouble. When you don’t get in trouble, however, you’ll love it.

Before explaining how to do it, let me give a few scenarios all centered around percentages:

  1. Have you ever wondered why you can’t write “1000*20%” to calculate the answer (200), but have to do “1000*0.2”?
  2. Have you ever wondered why we do the above and not the more readable “20% of 1000”?
  3. Have you ever wondered why we can’t calculate “1000 + 20%” and get the answer 1200? “1000 + (1000 * 0.2)” is obvious when you’ve learnt it, but wouldn’t it be more fun to have a shortcut such as “1000 +% 20"?
  4. Have you ever wondered why + always have to add numbers? Wouldn’t it be fun if it subtracts and gives your colleagues endless hours of tedious debugging? (The answer, obviously, is no; but bear with me here)
  5. Have you ever wondered why not everyone uses the metric system? How can you quickly convert lbs and stone (!) to kg or fahrenheit to celcius without resorting to complex calculation each time?

Well, you can’t do any of these in Perl 6 either. The good news is that you can implement your own operators so that you achieve what you want. Perl 6 gives you the power to define your own operators and even overload existing ones.

Operators can be of several kinds. prefix — ++$a (aka return $a and then $a = $a + 1); infix — 1+2; postfix — $a++(aka $a = $a + 1 and then return $a); circumfix — (1+2); postcircumfix — $a[2], etc. We’ll do the simpler ones here, and we’re sticking to real simple calculations.

Let’s do the simplest: How do we teach Perl 6 to interpret 20% as 0.2?

sub postfix:<%>($c) {
return $c * 0.01;
}
# say 20%; (would output 0.2)

How about writing 20% of 500 and get 100 as answer? Add this to the code above:

sub infix:<of>($a, $b) {
return $a * $b;
}
say 20% of 500; # Output: 100

Yes, I know this would break in many ways if your code is not as well behaved as the example above. But it’s meant more as an introduction than a bullet proof solution.

The solution to 100 +% 20 = 120 would look like this:

sub infix:<+%>($a, $b) {
return $a + ($a * $b%);
}
say 500 +% 20; # Output: 600

And if you really want to break a lot and not make sense at all, overload + so that it behaves like -:

sub infix:<+>($a, $b) {
return $a - $b;
}
say 80 + 20; # Output: 60 (!)

And conversions…? Define them!

sub postfix:<lbs>($a) {
return $a * 0.45359237;
}
sub postfix:<F>($a) {
return ($a - 32) * 5/9;
}
sub postfix:<stone>($a) {
return $a * 6.35029318;
}
say 100lbs; # Output: 45.359237..
say 8stone; # Output: 50.80234...
say 85F; # Output: 29.44444...

And just to round the whole thing off, so to speak: Have you ever wanted a shortcut to round it off like we humans do when making calculcations in our heads (i.e. round off to nearest half?)

Add this and try again:

sub prefix:<~>($a) {
return $a.round(0.5);
}
say ~100lbs; # Output: 45.5
say ~8stone; # Output: 51.
say ~85F; # Output: 29.5.

As I’ve often said in this series: Powerful stuff!

I guess my examples are not the most useful nor robust (as noted above). But when it comes to custom operators, it’s more or less a toe in the water.