Why Perl 6 isn’t Perl 5 & why you should care: a quick look at styles

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.

When I started programming professionally 20 years ago, Perl unexpectedly became my main language. After an initial period where I found Perl incomprehensible, a little annoying and just plain ugly — I read somewhere that someone called Perl a “write-only” language, but personally I found it unwriteable as well. One day, though, something clicked and all suddenly made sense. I learned to accept the sigil, to love regular expressions and all over appreciate Perl’s potential succinctness. Perl scripts could be written verbosely and readable, just as any other language, or it could be written unusually terse and effectively (probably one of the causes of the “write-only” label). I slowly fell into the latter way of writing Perl programs, and enjoyed my resulting effectivity.

Years passed and perl fell out of fashion; innovation seemed to be happening elsewhere. But I’ve always missed Perl and have continued to use it in all mye jobs (for a few yearsI was a CEO, and I even used Perl to analyse the monthly financial reports, something I’m willing to bet few CEOs before or after me has done).

Late last year, after one of my many periods wrestling with the straight jacket that is python, something drove me to google Perl 6. I wasn’t sure whether the language had manifested beyond the rumors and whispers I’d heard the last 15+ years. But I was pleasantly surprised to see that not only does it does exist, it has now reached a usable and reasonably reliable state through the Rakudo Star distribution (with perl6 the implementation and the language specification are two different things — with Perl 5 they’re one and the same — but for all practical purposes Rakudo Star is Perl 6 at the time of writing).

The main takeaway from my first meeting with Perl 6 was the discovery of what Perl 6 is not: Perl 6 is not Perl. At least not Perl 5. If you pick up Perl 6 and try some Perl 5 stuff, you’ll soon see why. More often than not the Perl 6 compiler will complain and croak. Here’s a couple of examples that shows how even the most simple stuff has changed:

bash$ echo “Output: The perl5 way” | perl -e ‘print while <>;’
Output: The perl5 way

The above code is probably among the simplest Perl 5 there is, and you would think Perl 6 — assuming version 6 supercedes 5 — would run this without problem. But try the same one-liner with perl6, and this is what happens:

bash$ echo "The perl5 way" | perl6 -e 'print while <>;'
===SORRY!=== Error while compiling -e
Unsupported use of <>; in Perl 6 please use lines() to read input, ('') to represent a null string or () to represent an empty list
at -e:1
------> print while(<⏏>)

What happened? Nothing much apart from the interpreter telling us that something’s wrong as well as gently guiding us towards the Perl 6 way of doing thing. For this to run in Perl 6 it should have been written like this:

bash$ echo "Output: The perl6 way" | perl6 -e '.say for lines();'
Output: The perl6 way

The similarity is that both Perl 5 and Perl 6 can complete the task using three “words”; strangely, though, none of the three are identical in the Perl 5 and Perl 6 examples. So a rose by any other name is not, no matter what Shakespeare claimed, still a rose. In reality Perl 6 is a language that only shares its name with Perl 5. Yes, it shares some of Perl 5’s most visible features (such as sigils etc) and philosophical underpinnings (“there’s more than one way to do it” as opposed to most languages that forces its users to adapt to its own style, i.e. Python).

Now, there’s a solution: If you import the module Inline::Perl5, you can mix Perl 5 code into your Perl 6 scripts. You can find similar modules for Python, Ruby, Go, etc. This can be handy for using modules and functionality that’s not available for Perl 6 yet, but it is — in my opinion — not a good way to ease transition from Perl 5 to Perl 6. Time’s best spent in learning the differences and functionality that’s unique to Perl 6.

When you have a graps of that stuff, you can apply the “more than one way to do it” philosophy to your programs. For me that’s the essence of perl-ism and Perl 6 has that in abundance. I thought I’d exemplify this trough showing various ways of solving a Euler problem — finding the smallest integer that’s divisible by 1..n (for example 2520 is the smallest integer that can be divided without remainder by 1, 2, 3, 4, 5, 6, 7, 8, 9 and 10).

The most verbose way to solve this problem is probably this:

#!/usr/bin/env perl6
my $n = 10;
$n += 10 while ($n % 2 != 0 || $n % 3 != 0 || $n % 4 != 0 || $n % 5 != 0 || $n % 6 != 0 || $n % 7 != 0 || $n % 8 != 0 || $n % 9);
say $n;

Of the styles/solutions I present here this one runs fastest. Incindentally it’s also the only one that’s Perl 5 compatible. But it’s also the least elegant. Should you want to test for 13 instead of 10 you’d have to edit the code and add $n % 10 through 12 as well.

A dynamic but ugly solution would be to use EVAL:

#!/usr/bin/env perl6
use MONKEY-SEE-NO-EVAL; 
sub MAIN(Int $divide-through = 10) {
my Int $n = $divide-through;
EVAL('$n += ' ~ $n ~ ' while (' ~ join(" || ",
map { '$n % ' ~ $_ ~ ' != 0' }, 2..$n) ~ ');');
say "The first integer that's divisible with 1..$divide-through is $n.";
}

It’s really not recommended to use EVAL, so discouraged that the program quits with a complaint unless you explicitly use the pragma MONKEY-SEE-NO-EVAL (Perl 6 has kept 5’s tendency to be charmingly silly at times). This pragma ensures Perl 6 that I know the risks associated with it, for instance unwanted code injection.

Ugly as it may be, the example demonstrates some of the important new Perl 6 stuff:

sub MAIN(Int $divide-trough = 10) {

MAIN is a special subroutine that’s invoked at the very beginning of a script’s run, a little like Java’s method public static void main(String[] args) or python’s if __name__ == “__main__”: run-time test.

Where it differs and becomes really powerful is that not only does it pick up parameters and/or arguments from the comand line, it also interprets and enforces them! This example says that the script can be called with an optional argument, and that argument has to be an integer. It even states that should the script be called without an argument, it will default to the value 10.

Perl 5 do not support this out of the box, but Perl 6 does and it’s really useful: If I try to run the program with something else, i.e. a string argument as in perl6 script.p6 TWENTY, perl6 won’t even run the script. Instead it outputs the following:

bash$ perl6 script.p6 TWENTY
Usage:
script.p6 [<divide-through>]

You really get a lot of functionality for free here, from parameter parsing to type checking of arguments. It makes me more secure using EVAL, I can actually rely on the MONKEY-SEE-NO-EVAL :-)

Perl 5 does not support MAIN; nor does it support explicit typing of variables (my Int blah); and EVAL is spelled eval. These are small differences that builds up to a large incompatibility. Even this short example shows how Perl 6 really isn’t Perl 5.

A very long explanation of a short “write-only” example

You don’t have to use EVAL at all. In fact you shouldn’t. Other solutions are more elegant, if not as fast running. Remember how I said that Perl 6 adapts to my style and not vice versa? What this means in practice is that I can make Perl 6 just as terse and “write-only” as any Perl 5 code I’ve ever written. I think this is an example of that:

#!/usr/bin/env perl6
-> $up-to {
gather {
-> $num {
take $num if $num && !(grep { $_ }, map { $num % $_ },
grep { $_ }, ^$up-to).elems;
} for ^Inf
}
}(10).first.say;

What does this code do, you ask? Exactly the same as the two examples above. This version goes trough every single integer from 0 to infinity and checks whether it divides without remainder for every integer from 1 through 10. It does that by using several Perl 6 tricks.

The code is written in a functional style. You don’t have to, but Perl 6 does it well should you want to. This script starts by defining an anonymous sub (or more specifically an anonymous block; what the difference is we’ll leave to another article). The sub takes exacly one parameter. As you see we don’t state type of the parameter explicitly here — one of the differences between anonymous blocks and subs is that the former doesn’t support explicily typing the parameter variable — so it’d be possible to chrash this code by submitting something else than an integer. Hardening the code is beyond the scope here, though, so let’s move on.

Everything between { } is part of the block. The block is closed in the final line and called with (10). I.e. it tells the block that we want to check integers for divisibility with 1 through 10.

The first line of the block uses the statement gather. Gather will collect values yielded from a loop into an array. This is a concept you may recognize from other languages such as Python and Javascript, the difference mostly being semantic — the Perl 6 equivalent of yield is take.

The next line defines another anonymous block, and this one takes one parameter — $num. The value of $num comes from a loop executed two lines further down by the for statement for ^Inf (it could also have been written for ^Inf -> $num for more clarity). Whichever way you choose — the for statement showcase another perl6 nicety: ^Inf or ^∞ if you will is shorthand for 0..infinity. You could do this for other values as well — ^65535 equals 0..65535 for instance. Should you want every odd number up to and including 65535, you could write that as 1,3…65535. Etc. The possibilities and shorthands are many in perl6.

for ^Inf is similar to creating an array with every number from 0 to infinity and looping through it. You could even explicitly create an array containing every number from 0 to infinity like this:

my @array = ^Inf;

You would think that this would produce an infinitely large array, but perl6 is smarter than that. It doesn’t produce a specific array element before it’s called; the array itself is empty until an element is accessed. And even when an element is accessed, perl6 only creates the elements needed. If you access @array[10] element 1..10 is created. If you later on access @array[65535] perl6 would populate the array with 11..65535; just enough for you to get what you want, but nothing more. This is really, really smart, and I exploit that kind of smart in my Euler code above. More on that soon.

The main logic of the code is this:

            take $num if $num && !(grep { $_ }, map { $num % $_ },
grep { $_ }, ^$up-to).elems;

This yields $num to the array that is being gathered, if a few things is true:

if $num is shorthand for saying that the test should only be run if $num is not 0 (^Inf returns an array that starts with 0; since 0 is divisable by anything, we’d always return 0 as the answer to every question; therefore we have to ignore 0 here).

The rest of the sentence uses a few tricks such as grep and map. If you read the thing backwards from .elems, you’ll see that ^$up-to returns an array with values from 0 to $up-to. The grep { $_ } to the left of it filters out 0 (the shorthand ^SOMETHING always starts with 0). I could have written 1..$up-to instead, or even better 2..$up-to -1, and omitted the grep. It would both have shortened and made the code faster, so why didn’t I do that? It has something to do with how readable this would have been as a one-liner, but more on that later.

The resulting array is then fed through a map statement.The map transforms the values in the array to whatever the result of $num MOD the values are. What we’re looking for are zeros. We use a grep to filter out the zeroes so we only end up with an array containing non-divisable elements. The !( ensures that zero-length arrays equals True. So if $num > 0 and $num modded against 1..10 returns 0 for every value, $num is taken by take and gathered by gather.

The first number this is true for is — as mentioned above — 2520. But what prevents the loop from going on for all eternity from there to infinity? How do we stop the loop? Well, we don’t, because here’s where the magic of producing array elements only on demand comes into play.

Take a look at the last line again:

}(10).first.say;

As I’ve already said the whole thing was started by the call (10). The next method is .first. That one says that what we’re interested is only the first element of whatever array is returned by the call. Perl 6 is intelligent enough to understand that because of .first it can break the loop and return after the first gathered number. I don’t have to write any kind of exit or break logic, perl6 takes care of all that tedious business for me.

Finally .say call the say method on the returned element, and the output is… 2520. I think it’s an elegant solution, although not as obvious as to what is does as the more verbose examples above are.

Variants

As I said above this code is the least efficient of the bunch. The reason being that I test too many divisors — mainly beacuse I want to be as terse as possible. It’s also caused by the fact that I test every number when I could have only tested every $num, $num*2…Inf instead. Again this has to do with terseness. The code started as a one-liner, and in that context I wanted to be obvious about one particular thing. Here’s the one-liner version:

bash$ perl6 -e '-> $up-to { gather { -> $num { take $num if $num && !(grep { $_ }, map { $num % $_ }, grep { $_ }, ^$up-to).elems; } for ^Inf } }(10).first.say'

If I wanted to reuse the one liner and test 13 instead of 10, it’s obvious what number I have to change — because written as it is, there’s only one number in the whole code. Wanting to be clear is the reason for writing less than optimal (and, ironically, unreadable) code in this particular case.

A few more examples

I’ve written a few variants of the above code. One is less verbose and the other is more verbose. Both are more efficient than the above version, and the most verbose is also the most effective (although none of these are as efficient as the very first — dumb — version).

#!/usr/bin/env perl6
-> $div-up-to, $test-num is copy = 0 {
gather while ($test-num += $div-up-to) {
take $test-num if !(grep { $_ }, map { $test-num % $_ },
^$div-up-to).elems;
}
}(10).first.say;

This uses a while loop to ensure that I only test numbers in intervals of n — i.e. the largest divisor we’re checking. This minimizes the amount of numbers we have to check, and decreases run time exponentially.

There’s one new thing to note here:

-> $div-up-to, $test-num is copy = 0 {

This block can potentially be called with a number telling the loop where to start. I.e. to speed up things further you could explicitly tell the routine to start cheking at for instance 2000. The default, however, is 0 so that if you omit stating $test-num explicitly, the code will run anyway. In this case I’ve omitted it.

Note also the use of is copy. By default parameter variables are unchangeable/immutable (not really — in a way they’re more like constant pointers, but that’s beyond the scope for now; just think of them as immutable and you’re good to go).

When you add is copy the variable is copied and consequently changeable/mutable. That enables us to use $test-num further down in the code without declaring and setting it explicitly elsewhere in the code.

The more verbose version is this:

#!/usr/bin/env perl6
-> $step, $num is copy = 0 {
while ($num += $step) {
if !(gather for 2..$step-1 -> $div {
if ($num % $div) {
take 1;
last;
}
}) { say $num; exit; }
}
}(10);

This does mainly the same thing as the above, but is — I would guess — a little easier on the eyes and to grasp intuitively. There’s no map method involved here, for instance. It turns some of the logic upside down as well.

If the code encounters a divisor with a remainder, it breaks the loop of divisors, and goes on to check the next $num for remainders. That reduces the number of calls. Here we also only check divisors from 2 through n-1, as we really don’t have to check the largest divisor when checking numbers that are incremented by n; if the loop starts at 0, every n will always be divisible by n and as such we don’t have to check it explicitly. The same goes for 1. This removes another two calls for every number.

And to finish it all off: Why would I want to use an anonymous block at all? Given what we have learned here, the following would probably be the best and most readble way of all (albeit not the fastest — the top spot still goes to the very first and most verbose variant when it comes to speed):

#!/usr/bin/env perl6
sub MAIN(Int $step, Int $num is copy where * % $step == 0 = 0) {
while ($num += $step) {
if !(gather for 2..$step-1 -> $div {
if ($num % $div) {
take 1;
last;
}
}) { say $num; exit; }
}
}

The anonymous function is replaced by MAIN again. But this time it’s got a few more tricks up its sleeve. Take a closer look at the first line:

sub MAIN(Int $step, Int $num is copy where * % $step == 0 = 0) {

As before we have an integer as first argument. Now I’ve made it mandatory. So you will have to call your script like this:

bash$ perl6 euler.p6 10
2520
bash$ perl6 euler.p6 13
360360

In addition you can give an argument stating at what number the loop should start, i.e.

bash$ perl6 euler.p6 13 300001
360360

But for the algorithm to be bullet proof it’s imperative that the number given (i.e. 300001) is divisable by the max divisor given (13), since the code assumes that it doesn’t have to check divisability by max divisor explicitly. I.e. it assumes that 300001 and all numbers following in increments of 13 would return 0 when modded with 13.

Normally I would have to write all kinds of checks explicitly, but not here. Perl 6 does most of the heavy lifting for us here.

Int $num is copy where * % $step == 0 = 0

…does what you need. Not only do you enforce that the argument is of type Int, you also give a rule for what values of Int are valid. In this case the integer $num modded by $step has to return 0 or else it’s an invalid value and you’ll not be able to run the script. This is really powerful — it’s type and value checking in one simple statement. Oh, and still you can make it a copy and give it a default value in case it’s omitted. Imagine how much elaborate code you’ll save using feature like these!

Closing words

This is just a quick look at some of the new stuff possible with Perl 6. The point of all this was not to be exhaustive in any way. We’ve not touched on the regexp syntax nor asynchronous stuff, custom operators, parsers, how big numbers can be written, how rational numbers stays rational (i.e. how 2/3 is treated as 2/3 and not an approximation such as 0.66666667), etc. I don’t even claim that I fully understand what little stuff I’ve shown here. But I hope this simple point has come across:

Perl 6 is a very powerful language that can do very much with very little code. As such it is worthy of the perl name. But the new stuff — which is great! — is very different from Perl 5; add that to the fact that most perl5 code will break in perl6, you have to see Perl 6 not as Perl, but practically a completely new language.

Much has been said about whether or not perl6 should have been named something different altogether. I won’t go into that discussion here, as that’s mostly a question for marketers.

Suffice it so say that if you’re a perl user, you’ll love Perl 6 as long as you don’t expect it to be Perl. Try it now!