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

As I wrote the title, an idea came to me. At the unix shell, anything other than 0 is an error and the number is the error code. Likewise, if I return an error string, then something went wrong in the function. Otherwise, just issue C<return> at the end of the function and Perl will promote the argument-less return to the appropriate form of nothingness based on caller context. Anyway, before I had my inspiration (written about above), I decided to use Date::Manip error return conventions.

Please critique this sub and tell me if you would do it differently.

sub ok { my $self = shift; my $email = shift or confess "where's the email?!"; my $err_ref = shift or confess "where's the err_ref?!"; if ( $self->{email}{$email} ) { $$err_ref = "duplicate"; return } if ( profane ($email, 'definite')) { $$err_ref = 'profanite/definite'; return } if ( $self->{blacklist}->optout($email) ) { $$err_ref = 'optout'; return } $self->{email}{$email}++; return 1; }

Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality

The Emacs Code Browser

Replies are listed 'Best First'.
Re: How to return errors from my object oriented subroutine
by perrin (Chancellor) on Aug 19, 2003 at 18:49 UTC
    I would typically throw an exception to indicate problems. You can bundle information about the exception up inside of something like Exception::Class.
Re: How to return errors from my object oriented subroutine
by dragonchild (Archbishop) on Aug 19, 2003 at 18:56 UTC
    Nit: if (exists $self->{email}{$email}) ... checking for truth creates the entry in the hash.

    Personally, I like throwing errors using die. That way, error propagation is done automatically by the interpreter instead of being handled explicitly.

    ------
    We are the carpenters and bricklayers of the Information Age.

    The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: How to return errors from my object oriented subroutine
by princepawn (Parson) on Aug 19, 2003 at 18:54 UTC
    Oh, well, maybe my semantics is off. I don't mean errors in the sense that something unacceptable happened... what I mean is that an email address is not to be considered useable by the system if it fails the above business logic (already used, profane, the person has opted out).

    Exceptions are typically used when something prohibits the system from continuing as it normally would, ie, some sort of serious error. I can't see uglying up the calling code by wrapping it in eval blocks and checking $@

    Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality

    Try this in VI: The Emacs Code Browser (sure to earn me a healthy supply of downvotes... heheh)

      Perhaps you have a distorted view of exceptions. I consider the "catch with eval {}" errors thrown by DBI to be a decent design, for example.

      Use exceptions when an error return value from the method call would be either inconvenient or impossible to check for.

      For a constructor, typically an undef value is returned for an exceptional case. For setters or getters, throwing exceptions seem to make more sense.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

      Personally, I consider them signals or messages. That a given message might be indicating that an exception occurred doesn't change the the fact that it's still a message from one subsystem to its caller. In fact, in some of my programming, I use a class hierarchy called Signal::*.

      ------
      We are the carpenters and bricklayers of the Information Age.

      The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      Okay, that makes more sense. In that case I would define a set of constants (although I would use globals for them rather the constant pragma) and use those as return values indicating the result of the test. These would be things like $EMAIL_OK, $EMAIL_PROFANE, $EMAIL_DUPLICATE, etc. The calling class can check them against the return value and take appropriate action.

      if ($email->check_validity() == $EMAIL_DUPLICATE) { send_error_page(message => 'that e-mail is already in use'); }
        I would actually have various tests to determine stuff.
        if ($email->is_duplicate) { ... } if ($email->is_profane) { ... } # etc ... package MyEmail; sub is_duplicate { my $self = shift; $self->validate unless $self->is_validated; return $self->{is_duplicate}; } # all the others are the same sub validate { my $self = shift; # set some attributes $self->{is_validated} = 1; }

        This way, you can check any of the possible error messages and the first time an error is checked, the file will be validated.

        ------
        We are the carpenters and bricklayers of the Information Age.

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

        if ($email->check_validity() == $EMAIL_DUPLICATE) { send_error_page(message => 'that e-mail is already in use'); }
        Now here we have to be careful not to mix display and business logic... this might be fine for now, but lateron if we want different UI's to the same business logic, eg. FTP service instead of web service, the send_error_page would have to be less UI-specific.

        But the focus is on clarifying the API of the email validation function, and I think the example is illuminating for that purpose.

        Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality

        The Emacs Code Browser

Re: How to return errors from my object oriented subroutine
by TVSET (Chaplain) on Aug 20, 2003 at 08:00 UTC
    I usually do something like this:
    sub do_something { my $args = shift; my $result = 0; # or "", or undef, or whatever my @errors = (); # Validate staff push @errors, "Where's the email?" unless ($args->{email}); push @errors, "Bad email" unless ($args->{email} =~ /\@/); # ... unless (@errors) { # Do something here # ... } return $result, @errors; }

    This approach provides a convinient way to display errors later on, if need be, while still validating provided data and saving on processing if ugly staff was given.

    sub display_status { my @errors = @_; if (@errors) { print "Operation failed due to following errors:\n"; foreach my $error (@errors) { print "\t- $error\n"; } } else { print "Operation succeded\n"; } }

    Now, if you need to display only errors you can do:

    my ($result,@errors) = do_something(\%args); if (@errors) { display_status(@errors); }

    If you need to show to the user an OK message too, then just call display_status(@errors) unconditionally.

    my two cents

    Leonid Mamtchenkov aka TVSET

      I would not return an array, but an array ref. First, it is more efficient, second, because an array is variable length, you can never easily tack on extra return values because the second expected value is not filling one slot in the return list. Using an array ref instead allows
      return $results, \@errors, \@thing_I_discovered_I_needed_later;

      Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality

      The Emacs Code Browser