Creating Your Own Perl
See what I did there? Unlike some functional programming languages, Perl doesn't have a built-in
reduce keyword, so I cleverly imported the
reduce function from List::Util. (Of course, if I'd been really clever, I'd have noticed List::Util also has a
sum function available.)
Due to some trickery with sub prototypes and manipulating its caller's symbol table, List::Util manages to make its
reduce function feel just like a built-in language feature. It uses the same codeblock syntax as
map, and the same magic
$b variables as
Via tricks like these, plus ties, overloads, custom
import functions, source filters, Devel::Declare,
%^H, and (in newer versions of Perl) the pluggable keyword API, Perl modules have the power to affect their caller in ways far beyond the mechanisms that other programming languages make available. When you use a module that does this, you're not just loading a library and using it at arm's length; you're changing the very syntax of Perl - lexically, within your module.
When starting a new script, or a new module, this is what we do. We add a bunch of
use statements to the top of the file to tweak Perl's flavour to our liking. We make Perl a more suitable language for getting the job done; we turn a general purpose programming language into a domain-specific language suitable for our exact task. This will often begin with something like:
but if you're writing anything non-trivial, it's likely that a bunch of other
use statements will join them.
(Of course, some modules are plain old object-oriented code that make no attempt to alter their caller's syntax. Different approaches are appropriate for different tasks.)
Twelve Lords A Leaping
Here are some of my favourite syntax-bending modules:
List::Util / List::MoreUtils
Many of these make creative use of sub prototypes to look and act like Perl's built-in list manipulation functions. The
reduce functions are especially useful, and should be in every Perl programmer's toolkit.
PerlX::Maybe provides a tiny function making it easier to work with optional named parameters, a la:
Syntax::Keyword::Junction implements support for something approaching the Perl 6 concept of junctions; that is, variables which have multiple values at once.
It achieves this with nothing more than careful use of overloading.
aliased provides short aliases for long class names.
The short alias is just a constant that returns the class name as a string. Simple idea, but useful.
Ever get the Can't call method "isa" on an undefined value blues? Safe::Isa gives you a way to call methods like
can on scalars without checking that they are defined and blessed.
It takes advantage of the fact that coderefs may be called as methods even on unblessed or undefined invocants.
$@ can be used as a try-catch mechanism in Perl, there are numerous gotchas. Try::Tiny works around them for you, giving you a nice syntax for exception catching.
There are even nicer modules like TryCatch available, but Try::Tiny's zero-dependency approach - it uses nothing more than prototypes and guards (dummy objects with just a destructor) - is perfect for even small projects.
NEXT adds a
SUPER-like pseudo-class to your module, but with more control of method redispatch than
SUPER gives you. Good if you're programming with multiple inheritance.
These days you should probably use mro instead, but NEXT deserves a mention for its clever use of AUTOLOAD and capitalised package names to create the illusion of new syntax.
This Plack-based web app framework uses a sub prototype hack for dispatching.
Perl's autovivification feature can sometimes be counterintuitive.
The autovivification module can selectively disable autovivification for particular scopes, or get Perl to issue a warning or fatal error when autovivification occurs. Very handy.
Lots of deep XS magic in this module.
Perl has various built-in quote-like operators.
qw() constructs arrays;
qr() quotes regular expressions and
qx() acts like backticks. PerlX::QuoteOperator allows you to define your own.
PerlX::QuoteOperator uses Devel::Declare to rewrite
qdeer qq(...) while Perl is compiling your code.
Function::Parameters provides parameter lists for Perl subs. Instead of:
You can write:
It supports named and positional parameters, optional parameters and methods with invocants. It provides an introspection API, and if you're using Moose, then it can hook into the Moose type constraint system to validate parameter types. Such
Function::Parameters uses Perl's new(ish) pluggaable keyword API, so is only available for Perl 5.14 and above.
Where to start? MooseX::Declare gives you
role keywords for declaring Moose classes and Moose roles;
is for inhertitance, role composition and meta traits;
method for declaring methods with signatures;
augment for method modifiers; and
clean for scrubbing away helper functions so that outside code can't call them.
It uses Devel::Declare. Extensively. And a partridge in a pear tree.
If you're working on a large project with many modules, you may find that you are repeating the same set of imports at the top of almost every file. Perhaps something like:
OK, so you can copy and paste, but copy-paste code is the enemy. Don't repeat yourself. Wouldn't it be nice to bundle up all the above functionality into a single module?
Well, here's an example of how you could write that module:
Alternatively, Syntax::Collector makes it a little neater:
Yes, that's a big quoted string (
q/.../), but no, it's not just
Bundling up imports into a single module makes it easier to encourage project-wide coding standards. You can't "forget" to enable warnings any more (but of course you can explicitly unimport it). You no longer have any excuse for using
ref when you mean
grep when you want
Bundling up imports allows you to consider ideas like true.pm which would seem ridiculous if you needed to repeat them at the top of every file, but become more appealing if they are included as part of an import collection.
And bundling up imports allows you to manage your project's dependencies from a single place. Don't want to depend on List::MoreUtils any more? Then write your own replacement for
uniq and get My::Syntax to export that instead. (The Syntax::Collector documentation includes examples of how to write a syntax collection that also acts as an exporter.)
So go on; create your own Perl. Make it your gift to yourself.