A couple of interesting comments to my
Five things I hate about Perl.
Quicksilver pointed out that I’d missed the difference between arrays and lists.
Yup, that’s pretty subtle, and of course it’s bitten me a few times (though oddly
enough, I don’t have that strong a dislike of it). Luqui on the other hand pointed
out that Perl is so much more fun if you do take the plunge and use
language-mutating modules, irrespective of the fact that they’re not standard.
I do agree, though it can sometimes be difficult getting to use these modules
for work, as everyone else has to buy in to the benefits and (perceived or real)
risks (learning curve, stability, speed, etc.).
So here’s an example of something that I’d forgotten I hated until I banged my
head against it in work’s codebase: AUTOLOAD.
AUTOLOAD like other languages’ “method-missing” feature, allows you
to dynamically respond to a method invocation without having explicitly defined
the method. So, for example, someone might call get_name on an object,
and the accessor for the ‘name’ field will be automatically be generated.
Actually, that sounds pretty cool, no? The problems are really in the implementation.
- AUTOLOAD subs are ugly. You have to do something like
use vars '$AUTOLOAD'; sub AUTOLOAD { my $sub_name = $AUTOLOAD=~/(\w+)$/; ... }
- Autoloading a sub is slower than normal dispatch. This isn’t a big deal of course in Perl,
as you can hack the symbol table. The first time the sub is called, you generate a coderef to deal
with the thing, then register that sub. On subsequent invocations, AUTOLOAD will never get called. - …Which does lead to the point that in very many cases you can just pre-generate a whole lot
of methods during late-compilation/early runtime. - If AUTOLOAD doesn’t handle a method, then what? It’s all too easy to forget to handle this case, and suddenly you have method calls that do nothing silently, an insidious and subtle bug.
- And if it didn’t handle a method, but the next class in the inheritance chain could have… tough — it’ll never get a chance to deal with it.
- You can’t use ‘can‘ to find out whether your object provides the method or not.
Yuck. Of course, someone has already dealt with most of the problem in a module: Ben Tilly’s
Class::AutoloadCAN. Using this module, you just declare a CAN sub, which may or may not return a coderef (the subroutine that will deal with the problem). Behind the scenes, overriding of the standard ‘can‘ meta-method and an auto-generate AUTOLOAD will automagically do the right thing. Yay!
The syntax isn’t that pleasant though. If we want to handle multiple autoloaded subs, we have to
stick them all into this CAN routine. I’d much rather do something like:
sub foo :handles(/^foo_(.*)/) { print "Fooing $1"; } sub bar :handles(/^bar_(.*)/) { print "Bar'ing $1"; }
So I thought, let’s use Attribute::Handlers, which lets us define these handlers. But I don’t really like Perl’s
attributes feature, and to be honest, I’m not keen on the redunancy of naming a sub and then the
regex that it handles.
I’d rather have some syntax like:
autosub (get_(.*)) { ... }
and of course that suggests Matt Trout’s beautiful
Devel::Declare.
And it’s surprisingly simple* to knock up a prototype AutoSub module which implements this. From the accompanying test.pl:
autosub (^take_(.*)) { my($what, @args) = @_; return "Took a $what"; }; autosub (^get_([^_]+)_(.*)) { my($adj, $noun, @args) = @_; return "Got a $adj $noun"; }; autosub (^do_the_.*$) { my ($what, @args) = @_; return join "," => $what, @args; };
* Where “surprisingly simple” glosses over a couple of points, noted here briefly for reference:
- Devel::Declare isn’t all that well documented yet. Basically, install_declarators takes 2 subs:
- add code at the beginning of the sub, e.g. for argument unpacking. We don’t use this, so we just return an empty string.
- install the subroutine reference. Other examples install this into the symbol table. We just add the sub into our list of @CANS.
- Yay for closures! Closing over @CANS means we don’t have to mess about with the name of the variable.
- Yes, I don’t really know what I’m doing with sub exporting. I think Ricardo Signes’s
Sub::Exporter would do the right thing, certainly with the CAN sub. But I’m not sure where to hang the Devel::Declare logic with it, I’ll investigate. - Oh, and there are certainly better ways of exporting the goodness of Class::AutoloadCAN into caller() than eval "package $package; use ..."
Update: mst pointed me at export_to_level, which is a standard flag handled by Exporter and other modules. But C::AC has a custom import… The other trick he suggests works: goto &Class::AutoloadCAN::import at the end of my custom import sub. - If you call a regex in list context, it returns a list of all the captures found. Unless there weren’t any captures, in which case it returns a list containing (1). This sucks, as I want to return the captured text in that case. (I guess I’d prefer it if it returned () but true, but that doesn’t exist in Perl…)
So right now, I’m checking if $1 was defined, and using the despised $& if not.
Suggestions?