autodie status update
I just looked through my blog history, and discovered the last release of autodie, I've blogged about was version 1.99! Since then, there's been nine releases! For those of you new to my blog, autodie is a lexical pragma that allows Perl's built-ins to throw exceptions on failure, which can take your error-handling from cumbersome to simple.
There's been a lot of changes in those nine releases, and the Changes file documents all the significant ones. There are nicer error messages, better coverage of Perl's core functions, fewer rough edges, and lots more tests. If you're not one to go reading Changes files, I can assure you that upgrading to 1.999 is pleasant and surprise-free.
I've had a few people tease me about the silly version number, 1.999 threatens to have 1.9991 as a next release, but this will not happen. The next version of autodie will be 2.000, and this actually represents a significant change. There's no backwardly incompatible changes, but there is a new feature which greatly extends autodie's ability to detect failures in non-core subroutines.
Presently, one can write:
use File::Copy qw(copy);
use autodie qw(copy);
copy($src, $dst);
If anything goes wrong, autodie spots it, throws an exception, and tells you file, line-number, source and destination files, and the error that occurred. Brilliant. However, one day, someone tried this:
my @array = copy($src,$dst);
Given that copy is going to succeed or die, I don't know why one would want to examine its return value as a list, but they did. The surprise here is that it didn't die on failure. It just silently ignored them.
The problem is in how autodie 1.9x currently determines failure from a user subroutine. In a scalar context, it considers a false value to be a failure, and in a list context it considers failure to be an empty list, or a list consisting of a single undefined value. The problem was that copy would return a list consisting of a single zero when called in list context[1], and that looked like a perfectly normal (success) value to autodie.
This is actually symptomatic of a deeper problem, which is that except when an exception is thrown, there are too many ways people try to signal failure in Perl. Why, just last week I saw code that did this:
return( undef, "1.21 jigawatts needed for operation" );
Yes, errors were signalled by returning a two-element list, the first of which was always undef, and the second of which was the error message. The function could also return a list of values which you were interested in, and there may only be two of those, but the first wouldn't be undef. To make things more fun, I've seen almost exactly the same signalling mechanism at a different client a few years back.
The only thing I haven't seen is anyone try to handle these failures, because they're such a pain in the arse to detect, nobody ever to tries. That means that then when things go wrong, they go badly wrong.
So, autodie 2.00 will have a "hinting interface", whereby you can say that these subroutines signal failure by doing this. The idea of hints are very simple: autodie smart-matches the return value against them, and if the result is true, a failure is considered to have occurred. Since it's possible to smart-match against subroutines, you can check for anything you want. The hint for the failure signalling shown above would look something like this:
sub { @_ == 2 and not defined $_[0] }
I owe Damian Conway a particular thank-you for spending a great deal of his time discussing hinting interfaces with me, and convincing me that there are just so many different ways people try to signal errors, that autodie really has to be ready for anything.
The other change is that when we build our exception (which doesn't need to be of the default autodie::exception class), it will also be passed the return value from the failing subroutine, which is useful not only for creating good-looking error messages, but which may also prove useful in logging and testing.
The actual hinting interface is described in more detail in my post to p5p and the accompanying thread. In brief, most end-users will never need to see nor worry about the hints, they'll Just Work.
All these changes will work on 5.8.x as well as 5.10.x, although there'll be a few minor limitations in 5.8.x, since I can't use a native smart-match under the hood.
The only thing I have to do now is finish writing the code, although there's a basic (but not yet final) working implementation on the hints branch of my git repo. The changes aren't actually that hard, I've just had lots of people throwing money at me recently to work on other projects. Luckily, I've got four flights in the next two weeks, and for me travel time is usually coding time.
[1]Actually, on Windows, File::Copy sometimes returns an empty string to signal failure, which makes things not only wrong, but inconsistently wrong! I've written a patch for this, but as I suspect I'm the only person this century to have noticed or cared, it hasn't been applied.