Perl6 looks pretty solid, from a language design standpoint. It cleans up a lot of the cruft people have long complained about in Perl, like changing the glyph in front of a symbol to change the way you're accessing that symbol, to support for (optional) strong typing of variables, and so on. It's a well designed language, and implementations, like Rakudo, are complete enough to be useful.
I decided to play around with the classic Factorial problem to test some simple implementations of the problem. First, I did a basic, naive implementation.
sub fact($n) { return 1 if $n <= 1; return $n * fact($n - 1 ); }
This is a simple solution, but as I said it's naive. It does a lot more work, since if you try to get all the factorials from 1 to 100, it has to recalculate each one individually, even though it's repeating a lot of work. But, Perl will memoize decently.
my @factorialCache = 1, 1; sub fact($n) { @factorialCache[$n] = $n * fact($n - 1) unless @factorialCache[$n]; @factorialCache[$n]; }
This does precisely the same thing, except that it caches runs, so that if I've already calculated the factorial of 5, when I go to do the factorial of 6, it's only two function calls, instead of 6. This is memoization, and it's a cool functional technique, basically creating a lookup table in real time, instead of pre-computing all the values.
A few notes about some of the differences between Perl5 and Perl6 that are present in this code, when I'm accessing elements of the array "factorialCache", I don't need to use the scalar ('$') glyph to access the array as a Scalar. The fact that I'm using the square brackets indicates I want scalar mode. Perl6 is a lot smarter than Perl5 on how I'm using variables. When declaring factorialCache, I also didn't need square brackets to declare it's initial values, this seems to only be required when you're using an anonymous array, such as passing an array to a function that wasn't declare prior, as will be in the next code example. Small things, but they do reduce the 'black magic' impression that some people have taken away from Perl in the past.
Of course, the @factorialCache is available globally in this implementation, but I was having trouble figuring out how to do recursion inside of a closure for creating a generic memoizer that would use closure. I tried this:
sub memoizer($function, @initialValues) { my @cache = @initialValues; return sub($n) { @cache[$n] = $function($n) unless @cache[$n]; @cache[$n]; } } my $fact = memoizer( { return $^a * $fact($^a - 1); }, [1, 1]);
This doesn't compile, however, as Perl tries to reference $fact in the anonymous code block, but $fact hasn't been initialized yet. The $^a
syntax is interesting, because it's a 'placeholder argument'. I could have assigned $a
by either my ( $a ) = @_;
or my $a = @_[0];
, but by using the caret, I'm telling Perl6 to pull those variables out of @_, in the order that I declare my placeholders. It's a tiny bit dangerous, but for small anonymous functions like this, it shouldn't be a problem.
In Perl5, this required something called the Y-Combinator. In Perl6, there is something to do with the &?BLOCK
keyword, but I'm having trouble getting that to work.
That may be Rakudo, since other things in the Perl6 spec aren't complete yet, such as using the state
keyword when creating an iterator using gather-take
syntax, which I was a bit disappointed to find didn't seem to work in a way that allowed for elegant memoization of this problem, but that may be me not fully understanding that feature as well.
It's rare these days I have cause to do much Perl. I've always been fond of the language, and I hope Perl6, with it's cleaner and more powerful syntax, can help bring more people back to the Perl community.