http://qs321.pair.com?node_id=748020

G'day learned monks,

Not that long ago, a bug against the autodie pragma (which makes subroutines and functions succeed or die) was reported here on PerlMonks.

The bug all comes down to context, and how subroutines report failure, and is best shown with an example:

use File::Copy qw(copy); use autodie qw(copy); copy($foo, $bar); # Void context, succeeds or dies. my $x = copy($foo, $bar); # Scalar context, succeeds or dies. my @x = copy($foo, $bar); # Array context, succeeds or fails silently.

Our problem is that the copy subroutine always uses return 0; to indicate failure. In a scalar context this is false, and autodie assumes it means an error. However in a list context it returns (0) (a list of a single zero), which looks like something which may be a legitimate value, and so autodie passes it through fine. By default, autodie only thinks a failure has occured if it sees an empty-list, or a list consisting of a single undef.

As this example shows, autodie can fail to detect failure when it occurs in unexpected ways. The solution is to provide a hinting mechanism, where not only can a subroutine be made autodying, but hints can be provided to control to describe what it considers to be a failure.

To keep the autodie interface clean, and to ensure nobody has to repeat themselves, these hints are provided out-of-band. So when you see a piece of code like:

use File::Copy qw(copy); use autodie qw(copy);

autodie will look up the hints table for File::Copy::copy and check to see which conditions indicate failure. A table of these hints for common modules will be included in the next autodie release, but they can also be supplimented by your own code (eg: use my::autodie::hints), or even built into exporting modules themselves. This all works right now on the hints branch of the source code repository.

Most of the time, end-users will never have to worry about the hinting interface, it's only something that myself, or module developers, or very eager people will be using. Having said that, I want to make sure I get it right.

Right now, the current hinting interface sucks. You have to do something like:

use Some::Module qw(foo); use autodie::hints qw( LIST_EMPTY_ONLY SCALAR_UNDEF_ONLY ); autodie::hints->set_hints_for( \&foo, LIST_EMPTY_OR_FALSE | SCALAR_UNDEF_ONLY );

Note that we're OR'ing bits together manually to set the hints. That was due to an old idea that came to me on a coffee-deprived tram ride, and which didn't work out. Note that we're also including some big ugly constants just to use them in a single call. I don't like that at all.

So, I'd like to replace the interface. Currently the plan is to have something like this:

use Some::Module qw(foo); use autodie::hints; autodie::hints->set_hints_for( \&foo, qw( LIST_EMPTY_OR_FALSE SCALAR_UNDEF_ONLY ) );

Here we're passing in a list of strings as hints, which makes error messages nicer (you typed X, did you mean Y?), and avoids having to screw around with bitwise operations. Settings currently have the first word being the context they apply to (scalar/list), and then we list what are considered 'failure' values. The current list are as follows:

Note that there are some things which are missing from the list, my goal is to have autodie work with the most common legacy ways of signalling errors, not every possible wacky scheme imaginable. Also note that the hinting mechanism will never be used for Perl's built-in functions, autodie is already aware of their special cases, and no further user intervention is necessary.

So, my question to you, dear monks, is can I do this better? Can the hints be given better names? Is my set_hints_for method particularly unintuitive? How would you expect this to work?

The autodie module is going into the 5.10.1 release of Perl, and as such if I screw things up, it's likely they'll stay screwed for a long time. Any comments, feedback, or questions are appreciated.

Many thanks,