I have tried quite a number of programming languages, mostly out of curiosity than anything else. Even so, Perl was something I always steered clear of. Traumatic first experience, terrible reputation and all that. And apparently, so did a lot of people. It’s 2016, and Perl’s prominence isn’t close to what it used to be in its heyday. Fads pass, even empires fall. Except, it would be unwise to equate this with poor quality. No tool hits every sweet spot, that’s why we have so many of them. And adoption depends on so many other unrelated things. Think of npm, or don’t if you care about sanity.
Anyway all these are barely related to what I want to write about which is Perl 6, Larry Wall’s utopia. The major distraction that annoyed and divided original user base. Something that - to others - gradually attained the right to be elevated to the illustrious ranks of Duke Nukem Forever, NetHack, Half Life 3 or GNU Hurd. Years rolled by while several implementation attempts got abandoned, from the outside it looked exactly like a meme.
And it says a lot about the vision and dedication that it finally became a reality in the form of Rakudo at the end of 2015, a good 15 years after the first proposal. Granted it’s just the first release, as such refinements like speed of execution, library and community size, tool support etc. leaves a lot to be desired. But it’s feature complete with respect to the spec, and that’s really something to be talked about.
The nomenclature gives one the impression of Perl 6 being the evolution, and that might have been the original motivation, but it’s really not. It’s a full scale revolution. My limited exposure doesn’t make me an authority, but I can draw subjective comparison based on everything else that I have used. Perl 6 kinda borders on magic.
It’s a big language, lots of features. It’s modern and it’s multi-paradigm. What’s so beautiful about it is that every thing comes around to complement everything else so well. So you have an all powerful Swiss army knife, but this time it’s also elegant. To spare a few words on the features: it has gradual typing so you can mix and enjoy the best of both static and dynamic typing, concurrency/parallelism/async is built into the core and is accessible at a high level of abstraction, it has built in grammars and polished first-class regex, lazy evaluation, great OO model, full grapheme based unicode, clear scoping rules and closures, multiple dispatch based on signature, pattern matching and de-structuring, metaprogramming, native data types and easy interpolation with native code (you can even inline Perl 5 and python code), safety features like bounds check and rational numbers and so on. I don’t care for them all, and maybe you don’t either. But the point is, when the going gets tough, it will likely live upto the challenge.
There are lots of great and fancy up and coming languages out there. I have a keen interest in most of them. And beauty is in the eyes of beholder, so I won’t go there. But what almost all languages completely miss is the aspect of ergonomics, the attention to a programmer’s productivity as he solves a wide variety of problems. Maybe it’s the long years in the dark along with the amazing people behind the veil and their desire to get it right even if it’s very late and things are needed to worked out from the scratch yet again, but they got it right which is incredible given the breadth of it.
Give it a try for yourself. For quick intro:
- http://perl6intro.com/
- https://learnxinyminutes.com/docs/perl6/
- https://en.wikibooks.org/wiki/Perl_6_Programming
Or if you prefer examples:
- http://examples.perl6.org/
- https://perl6advent.wordpress.com/ (Advent style blog posts from every year since 2009)
- https://github.com/sillymoose/Perl6-One-Liners
For a few appetizers that focuses more on the language, I solved the first few project euler problems (you would want to read the questions to follow along):
Problem 1
say [+] (^1000).grep: { !(($_ % 3) && ($_ % 5)) };
Things to note:
say
is theprint
equivalent routine.[]
in this context is thereduce
meta-operator which here took an infix operator(+
). Acting on a list of numbers this basically does the job ofsum
. You might also know it as Fold.- The syntax for iterable range construction is
1..10
. The range is inclusive, you may tell it to exclude the last element with1..^10
.^10
is essentially a short hand for0..^10
. - The routine
grep
filters a sequence based on a pattern provided. The pattern is given in the form of ablock
which is akin to a closure (anonymous function with environment). The colon after grep allows you to pass the block without needing parenthesis, it’s a handy short-cut (explained here among others). So these two are the same:(1..10).grep({$_ mod 2 == 0})
and(1..10).grep: {$_ mod 2 == 0}
. If you need to pass an argument to it, you can do so by-> $x { $x }
. The$_
in{ $_ }
is just a syntactic sugar for the parameter passed. - The body of the function is just a cheap boolian trick.
Problem 2
sub fib($limit) {
my ($last, $new) = 0, 1;
while $last < $limit {
take $last;
($last, $new) = ($new, $last + $new);
}
}
say [+] (gather fib 4_000_000).grep: * %% 2;
Things to note:
sub
is how you define functions. Nice bit of pattern matching in the variable definition and also inside the loop. Note that even the while loop works on a block, this is very prevalent construct.- The
gather/take
is the iterator builder pattern. Together you get the effect of laziness, somewhat likeyield
of Python. Note that the gather needs not be in the lexical scope of take, dynamic scope will suffice. - Underscores in numbers are allowed because they add readability.
(x %% y)
is a short cut for(x mod y == 0)
.- And yes, then there is the
*
. It’s really such a simple concept, yet so bizarre. Despite that, it’s already one of my favourite aspects of the language. But before I explain, here is a ridiculous alternate solution:
say [+] (0, 1, * + * ...^ * > 4_000_000).grep: * %% 2;
That’s just bonkers! To de-mystify the *
which in
Perl 6 parlance is known as Whatever
type. Basically:
Everything that can’t be categorized otherwise fits into the “Whatever” slot, or as the Perl 6 hacker would write it, *.
Essentially the Whatever-Star
stands for whatever it makes sense in the context it appears in. So its
semantics is context dependant. When used in (1..*)
it constructs an
infinite range. If you have an array named @x
then @x[*-1]
indexes
the last item because * represents the length of the array. Or in
(1..10).grep: * %% 2
it basically de-sugars to
(1..10).grep: -> $x { $x %% 2}
.
In this example above, it defined a series. Consider 1, * + 1 ... *
.
Here the first star represents the number just prior to it in the
series, so basically it means the second number is one added to the
first number. The second star means this series goes on to infinity. So
0, 1, * + * ... *
is a series where every number is the sum of the
past two numbers, and the first two numbers are 0 and 1. That’s what
Fibonacci is! The second part * > 4_000_000
represents a goal,
instead of infinity this now terminates the list after 4 million. ...^
makes this exclusive. You can think of it as takeWhile
from Haskell.
The result is an extremely elegant definition, you just describe what
Fibonacci is!
Problem 3
sub largest-prime-factors($num is copy) {
for 2, 3, *+2 ... sqrt $num {
while $num %% $_ {
$num div= $_;
return $_ if $_ > $num;
}
}
}
say largest-prime-factors 600_851_475_143;
Not much of interest in here. By default parameters are immutable which
is a nice default. Here I used is copy
trait to make certain that the
argument is copied, and therefore mutable. Also note the lack of
parenthesis in the function call.
Problem 4
((999...800) X* (999...800)).grep({ .flip eq $_ }).max.say;
Things to note:
- The
X
is an infix meta-operator forcartesian product
. It can take another operator soX*
multiplies each crossed together elements. I used lower bound 800 because otherwise the whole computation takes too long. .flip
is a sugar for$_.flip
.- Perl 6 has UFCS (Uniform Function Call Syntax). You can chain different functions together as if you are calling methods.
Problem 5
say [lcm] 1..20;
Only thing to note is that there is a built-in lcm
routine.
Problem 6
my $nums = 1..100;
say (([+] $nums) ** 2) - ([+] $nums.map({$_ * $_}));
Further use of $_
, this time twice. Also map
, and it behaves as
expected.
That’s all for now. I sure could get used to writing this.