Creating Your Own Perl

Perl modules are not like dynamically loaded libraries in other programming languages. Thanks to the import function, sub prototypes, symbol table hacking, parser hooks, magic like Devel-Declare, ties and other voodoo, Perl modules can shape and craft the flavour of Perl that is available to their caller. A practical example: Perl's exception handling via eval and $@ is weird, clunky and error-prone. But by loading TryCatch or Try::Tiny you get a clean syntax for catching exceptions that Just Works. You're not just loading a library and using it at arm's length; you're changing the very syntax of Perl - locally, within your module.

(Aside: there are of course plenty of modules that don't do any of this - say those that are designed to be used in an object-oriented fashion. Those are great too of course - different approaches are appropriate for solving different problems.)

Exception handling is not the only example. List::Util, Plack::Builder, Moose, MooseX::Method::Signatures, syntax (with its friends) and pretty much every module that uses Exporter or similar all fall into this broad category of modules that shape the flavour of Perl being used.

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 use 5.010; use strict; use warnings;, but if you're writing anything non-trivial, it's likely that a bunch of other use statements will join them.

Which brings me to Syntax::Collector. The idea of Syntax::Collector is that in a large project which spans multiple Perl modules and scripts, there are certain flavour of Perl that we wish to apply across all files. That might be use strict and use warnings to ensure good practice is followed, and might also include use Scalar::Util qw(blessed) or use List::Util qw(first reduce) or use Try::Tiny. Syntax::Collector helps you capture that flavour in one module, and use that module in each script you write. Here's an example:

package MyProject::Syntax;
our $VERSION = 1;
use Syntax::Collector -collect => '
use strict 0;
use warnings 0;
use true 0;
use feature 0 qw(say state switch);
no warnings 0 qw(void once uninitialized)
use Scalar::Util 1.23 qw(blessed);
use List::Util 0 qw(first reduce);
use Carp 0 qw(croak);
use Try::Tiny 0;
';

(Aside: you might be horrified that Syntax::Collector's list of modules to import is a simple quoted string. Don't worry; we don't eval it. It's parsed and handled fairly sensibly. The purpose of using the Perl-like syntax within the string is to be friendly to people grepping through source code for use Some::Module.)

Then in each file of the project, we just use MyProject::Syntax 1 and all the rest of that stuff is automatically imported. This not only has the advantage of reducing boiler-plate code, it can change the way you write Perl. Let's imagine that we're trying to find a parser that can handle HTML input:

my ($htmlparser) = grep { $_->does_accept('text/html') } @parsers;

That looks fine, and we might be happy with it, but if @parsers is a long list, then it's potentially pretty inefficient. After we've found the first HTML parser, we don't want to keep grepping through the list. The first function from List::Util would be much better:

my $htmlparser = first { $_->does_accept('text/html') } @parsers;

But if we're working on a module, and this is the only time we need first then we might sigh, ho hum, grep will do. We'll die when we really should croak. We'll check defined ref instead of blessed. But with Syntax::Collector, and all these functions loaded up-front, we're encouraged to use the right tool for the job.

Another advantage of Syntax::Collector is that it gives us a single place to declare all the project's dependencies. Later on, if we want to reduce dependencies, we have a single place to peruse the list. If you notice that the only part if List::MoreUtils that you're using is uniq then you can write your own uniq implementation (it's a one-liner) and use that instead. And you don't need to go through all your source files replacing references to List::MoreUtils, because the only reference is in your syntax module.

Syntax::Collector 0.004 is on CPAN.

1 Comment

This is very cool. I've been doing a similar kind of thing where I hacked up a custom Devel::Declare class to give me a custom class { ... } keyword that set up all my preferred syntax within the block. (TryCatch, List::Util, autodie, etc.) but your approach of using Module::Runtime seems a bit saner.

Leave a comment

About Toby Inkster

user-pic I'm tobyink on CPAN, IRC and PerlMonks.