develooper Front page | perl.perl5.porters | Postings from April 2009

RFC: Autodie 2.0 interface sanity checks

Thread Next
From:
Paul Fenwick
Date:
April 25, 2009 00:03
Subject:
RFC: Autodie 2.0 interface sanity checks
Message ID:
49F2B5A8.8090903@perltraining.com.au
G'day p5p,

I'm working on autodie 2.0, which I hope will make the 5.10.1 release.  The
interface contains a few important changes, and I'm hoping to have them
sanity checked before I start releasing them on the world as whole.  I'm
looking for feedback one everything and anything; if I mess up the
user-interface now, it's really hard for me to go back and fix it.

== What's autodie 2.0? ==

The defining feature of autodie 2.0 is that it provides a rich hinting
interface for user-defined (non-core) subroutines.  "But wait!", I hear you
cry, "I can already do that with the current autodie..."

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

	copy($src, $dst);	# Automatically dies on failure

Well, you're right, but the old autodie had a very simplistic idea as to
what's considered failure, which was restricted to only three cases:

	* A false value, in scalar context.
	* An empty list, in list context.
	* A list of a single undef, in list context.

Unfortunately, not everything uses these values to signal failure; for
example, File::Copy can return a list of a single zero to signal failure[1].
 At my last two consulting gigs I've seen code that returns (undef, $error)
to signal failure.  I'm sure there are many more wacky ways that code on the
CPAN and DarkPAN can fail.

== What does the hinting interface look like? ==

The idea of the hinting interface is that we can provide *hints* to autodie
as to how certain subroutines or classes or subroutines fail.  Currently
using my development code[2] the interface looks like this:

	use autodie::hints;

	autodie::hints->set_hints_for(
		\&foo,
		{
			scalar => SCALAR_HINT,
			list   => LIST_HINT,
		}
	);

It's possible to pass either a subroutine reference (recommended) or a fully
qualified subroutine name as the first argument, so you can set hints on
modules that *might* get loaded, but haven't been loaded yet.

The hints above are smart-matched against the return value from the
subroutine; a true result indicates failure, and an appropriate exception is
thrown.  Since one can smart-match against a subroutine, it's possible to do
quite complex checks for failure if needed.  Kudos to Damian Conway for
suggesting the idea of using smart-match against hints.

The hint-setting interface is pretty verbose, and I don't expect to see
typical end-user code setting hints.  It's the sort of thing that should be
written into sub-classes (my::company::autodie), or modules (preferably next
to the subroutines themselves).  There will be a number of constants defined
for common cases.

On 5.8, only simple scalars, arrays, and subroutines will be supported as hints.

== Insisting on hints ==

One thing I'd *really* like to have is a way to insist that a user-defined
subroutine actually has hints.  If autodie has to guess, then it can guess
wrong, and autodie is supposed to be about making code simpler and easier,
not introducing difficult to track down bugs.

My dilemma is how do I make make a simple, intuitive, and lazy interface
that allows the end-user code to insist that hints have been specified.  I
don't want to break compatibility with existing code, so I don't want the
requirements for hints to be turned on by default.  I am thinking of
something like:

	# foo() and bar() must have their hints defined
	use autodie qw( !foo !bar baz );

In the spirit of Don't Repeat Yourself, I'd also like to use:

	# Everything must have hints.
	use autodie qw( ! foo bar baz );

While this fulfils my requirements for simple and lazy, it's not intuitive.
 I'd really love suggestions on improvements if you have any.

== Auto-finding hints ==

The final piece of the hinting interface is how I envision most hints to be
provided.  Rather than having modules call autodie::hints directly (which
requires the module be loaded), the preferred method is for modules to
implement an API that ensures hints can be easily found:

	package Your::Module;

	sub autodie_hints {
	    return {
	        foo => { scalar => HINTS, array => SOME_HINTS },
	        bar => { scalar => HINTS, array => MORE_HINTS },
	    }
	}

The big advantage here is the hints can be in the same file as the code they
describe, but without any requirement that autodie be present or loaded.
Questions, and any feedback on what the subroutine name should be called is
appreciated.

Many thanks for all your time!

	Paul

[1] Except on Windows, where the copy() and cp() functions return a list of
an empty string instead.

[2] It's the 'hints' branch on my autodie repo on github.
    http://github.com/pfenwick/autodie/tree/hints

-- 
Paul Fenwick <pjf@perltraining.com.au> | http://perltraining.com.au/
Director of Training                   | Ph:  +61 3 9354 6001
Perl Training Australia                | Fax: +61 3 9354 2681

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About