zrajm has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing a small module for checking whether or not a phone number is correct, and I'd like to be able to return informative error messages, such as "Missing area code" and similar. But how do I do this in a neat manner?

The function is to be used in a web form, and it returns a cleaned-up version of the phone number on success (or the empty string, if the user didn't enter a phone number).

I'm guessing that what I want to do when the number is incorrect in some way is to return undef, and set $@ to something explanatory (which then could be given as user feedback in the webform).

But how do I do this nicely? Redefine die() (add some kind of __DIE__ hook?) or write my own sub to call on failure, which returns and sets $@ (so that my checks may still read e.g. /^$/ or fail "no number"?

I read perlmodstyle ("Error handling and messages"), but I could find no answer to my question there.

Replies are listed 'Best First'.
Re: How to return an appropriate error from module?
by wfsp (Abbot) on Mar 05, 2009 at 14:09 UTC
    I don't count 'user errors' as errors. So I wouldn't mess with die (I keep that for errors in the code).

    What I often do in these cases is for the method/sub to return a hash.

    ( success => 0, data => q{whatever}, message => q{something horrible happened}, )
    my %result = validate($arg1, $arg2); if ($result{success}){ # woot! we can continue # do something with $result{data} } else{ # try again send_screen_back($result{message}); }
Re: How to return an appropriate error from module?
by Bloodnok (Vicar) on Mar 05, 2009 at 14:17 UTC
    You might want to consider exceptions (throw(), try{}, catch{} blocks et al) - have a look at one of the many exception related modules - we're in the process of adopting a combination of Exception::Class & Error.

    A user level that continues to overstate my experience :-))
Re: How to return an appropriate error from module?
by JavaFan (Canon) on Mar 05, 2009 at 14:15 UTC
    One easy way is to return false if the number is correct and a reason (either a string, or a number - the latter has the advantage of defining error strings later - perhaps localized) if the phone number is invalid.

    Or you could return an object that is overloaded so if the phone number is valid, the object is true in boolean context, and if the phone number is invalid, the object is false in boolean context, and gives the reason in string context.

    But dying works as well. The caller should catch all calls though, which makes you do more work if only you're interested in valid/not-valid and don't really care about the reason.

    Many options to choose from. None is "the best".

Re: How to return an appropriate error from module?
by locked_user sundialsvc4 (Abbot) on Mar 05, 2009 at 21:36 UTC

    As a (further...) general comment, this is exactly the sort of situation where I find it very useful to employ Perl objects. (As many CPAN modules do.)

    Basically, what you do is to “instantiate a new Perl object,” then “call a method to determine the result.” The method-call will dutifully return a response that is true-or-false ... but because you are talking to an object, the method can also quite-easily store an error-code for your subsequent retrieval.

    my $foo = My::Verifier::Module->new(); if (!$foo->validate($number)) { print "You can't do that because $foo->error_message\n"; };
    It's easy to understand, it's reasonably efficient, and it works well in practice. The “overhead” is actually quite negligible, and I find myself employing this metaphor for a lot of things. A Perl object turns out to be a very handy “container for context.” You can instantiate it, use it for as long as you need to, and then just let it go ... without feeling guilty.

Re: How to return an appropriate error from module?
by ig (Vicar) on Mar 05, 2009 at 16:04 UTC

    You may already have reviewed previous discussion on checking phone numbers. There are many existing modules dealing with phone numbers and you might want to adopt the error handling style of one of them.

Re: How to return an appropriate error from module?
by ikegami (Patriarch) on Mar 05, 2009 at 14:57 UTC
    The interface you asked about is provided by:
    sub validate_phone_num { return eval { /^$/ or die "No number\n"; ... $cleaned_up_num }; }
      I like this idea.

      Is there any rule/praxis for or against setting $@ inside my modules or subroutines -- as a way of returning error messages?

      If using above example as a guide, would the following module be considered to be "kosher" perl?

      package Phone; sub prettify { local ($_) = @_; return eval { /^$/ and die "No number\n"; # do something to modify $_ here $_; }; }

      In my main program this could then be called using:

      my $pretty_number = Phone::prettify($number) if (not defined($pretty_number)) { # e.g. insert message $@ into web page output }
        It is a bit odd. Why not let the exception propagate?
        package Phone; sub prettify { local ($_) = @_; /^$/ and die "No number\n"; # do something to modify $_ here $_; }
        my ($pretty_number) = eval { Phone::prettify($number) } or do { ...insert message $@ into web page output... ...exit/return/next/etc... };
Re: How to return an appropriate error from module?
by jrw (Monk) on Mar 05, 2009 at 22:16 UTC
    You could return a tuple of (ERRMSG, VALUE). This makes it easy to return errors. It has the benefit of being very lightweight. Here's a script for testing:
    #!/usr/bin/perl -w use strict; sub chk_phn { local ($_) = @_; return "missing input" unless defined $_; return "empty input" if $_ eq ""; return "invalid character" if /[^-\d]/; s/-//g; return (undef, $_); } my ($err, $res) = chk_phn @ARGV; $err = "UNDEF" unless defined $err; $res = "UNDEF" unless defined $res; print "<$err><$res>\n";
Re: How to return an appropriate error from module?
by Anonymous Monk on Mar 06, 2009 at 02:32 UTC

    Funny, I'm coding a schema validator too at the moment and returning hash (more precisely, hashref) like {success=>1, data=>..., ...} is also my preferred way.

    Compared to the ($success, $res) tuple, hash gives you more flexibility as you can add more keys laters should you need one.

    I'm too lazy creating "result objects" and I think it's an overkill anyway. Oftentimes you just want to give the user an error code and the output without much behaviour at all, so object is not particularly necessary here.

    Exception is actually very handy, but I often deal with cross-language, cross-computer communication and convention about remote exception is not that well defined (at least in Perl?). Also I've always had this feeling that Perl5 is not really "exception-based". I'd wait for Perl6 for that, but that's just me of course.

    A simple data structure like hash, on the other hand, is supported by virtually any decent programming language (even bash nowadays, I've heard).

    There's another style which you could try:

    $success = validate($data, $output);

    $output is an optional argument which if given will be filled with the output by validate(). Sometimes you don't need the output at all and just need the fail/success boolean status.

Re: How to return an appropriate error from module?
by locked_user sundialsvc4 (Abbot) on Mar 05, 2009 at 15:57 UTC

    “Do not do a thing already done.” Check CPAN.

Re: How to return an appropriate error from module?
by leocharre (Priest) on Mar 09, 2009 at 14:38 UTC
    First; how it should work: Let's imagine your module is Food::Product. Food::Product creates instances (objects). Let's try to instance a food product.. (code untested)
    use Food::Product; my $p = new Food::Product('Tomato') or die("Could not instance Food::Product: ($Food::Product::errstr)" +); $p->isle(14); $p->stock(450); $p->sell(450) or die($p->errstr); $p->sell(40) or die($p->errstr);
    Example of what is inside Food::Product
    package Food::Product; use strict; use LEOCHARRE::Class2; # you do this your way use vars qw/$errstr/; __PACKAGE__->make_accessor_setget(qw/isle stock errst/); sub new { my($class,$arg)=@_; $arg or $errstr = "Missing valid arg" and return; return { name => $arg }, $class; } sub sell { my($self,$count)=@_; $count or $self->errstr('missing how much to sell ammount') and return; my $now = $self->stock or $self->errstr('no stock left to sell from') and return; my $left_over = ( $now - $count ) ... some other rationalle $self->stock($left_over); }

    So in above examples.

    • $Food::Product::errstr is a class symbol holding error string (potentially). These are class wide errors- maybe on compile, some config file should be present in the system- etc.
    • Food::Product::errstr() is a method for the object- a regular perl setget method. This hold errors in regards to this one object. If you give it a bad name, a faulty parameter.. etc.

    Check out DBI, has some similarities.
    I used to fumble with this, wondering if maybe $error was better a label.. but errstr is pretty freaking common.

    So, to answer more directly. In c, it's common to return if something is up- in perl, we return when it's all good. So, store any messages in the class or object as a separate value. Do NOT assume the coder wants to access further information if something fails. Just make it possible to get more information. Whatever you do- don't return only if there's an error etc- that would be poor form in perl.