Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Using die() in methods

by v_melnik (Scribe)
on Jul 06, 2014 at 16:37 UTC ( [id://1092477]=perlquestion: print w/replies, xml ) Need Help??

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

I have the error attribute for every class for explaining to the caller what's gone wrong. And the caller should do:

my $result = $object->method(); unless(defined($result)) { $log->error("Something's gone wrong when called $object->method(): + " . $object->error_message); # ...doing something with that... }

When I need to return undef as a result (for example, if the method hasn't found the result - not because of an error occuried), I have to do something like that:

my $result = $object->method(); if($object->has_error) { $log->error("Something's gone wrong when called $object->method(): + " . $object->error_message); # ...doing something with that... } # ...doing something else, understanding that the method just doesn't +have anything to pass to us...

Are there people who prefer to pre-suppose all their methods are dying every time when something goes wrong, so all methods should be called through eval{}/try{}/...?

Sometimes I'm thinking about it as about a better way. For example it would allow me to return undef when nothing wrong have happened, so I'd prefer to die() with some explaination that would be accessible as the $@ variable.

So I consider about something that would look like that:

my $result = eval { $object->method() }; if($@) { $log->error("Something's gone wrong when called $object->method(): + $@"); # ...doing something with that... } # ...doing something else, understanding that the method just doesn't +have anything to pass to us...

Maybe it would be better to try some of TryCatch mechanisms from CPAN.

Anyway, using die() from the method really looks a bit odd. Who really does that? Have you been always doing it that way or started to do it that way some time ago?

Thank you!

V.Melnik

Replies are listed 'Best First'.
Re: Using die() in methods
by moritz (Cardinal) on Jul 06, 2014 at 16:47 UTC
    Anyway, using die() from the method really looks a bit odd.

    It looks pretty normal to me.

    Who really does that? Have you been always doing it that way or started to do it that way some time ago?

    Lots of folks do it; it's how you're meant to handle errors, after all. Modules like autodie bring it to core functions in perl that were written before exceptions existed.

    Anyway, exceptions are useful if they don't need to be always caught in the caller, but somtimes in the caller's caller, or higher up the call chain.

    The second main benefit from exceptions is that if somebody forgets to handle them, it'll be very obvious. On the other hand if things go wrong and nobody notices, much more harm is done (say writing a config file fails, and thus work is silently lost, and nobody notices until much later), and it's much harder to debug.

Re: Using die() in methods
by boftx (Deacon) on Jul 06, 2014 at 17:21 UTC

    My general approach is to die when the error is unlikely to be recoverable from, such as a coding error (i.e. missing or invalid parameters, etc.) and to return undef if it is something like a record does not exist. You can find several examples of this in modules on CPAN.

    You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
      Yes, that sounds like a rather reasonable approach to the problem. One thing I really dislike about some languages, such as PL/SQL for example, is the way they throw an error when no data is found, where it would be perfectly legitimate to use a select statement to see if a record exists and then, depending on the result, to either create a new record or to update an existing one. Of course, the exception can be trapped, for example by using a separate procedure with an exception-catching mechanism, but it makes the flow of the program much more complicated with very little benefit. This is a bit as if you had to use eval every time you need to check if a hash value exists or is defined.

      Failing on an outright error (such as invalid parameter) and returning undef (or some other specific status) when no data is found is much more reasonable.

Re: Using die() in methods
by Anonymous Monk on Jul 06, 2014 at 17:17 UTC

    It's a TIMTOWTDI and philosophical issue as to how your API returns errors - just make sure your API is consistent once you choose an approach! The first two of your code examples are pretty common ways of handling it. On the other hand, the "user has to check an additional variable after calling my method" design is not to my personal taste, even though it's a classic (e.g. errno).

    If the error condition is something that happens relatively often, do you really want to force a user to eval (or equivalent) all of your methods (personally I wouldn't), or is it really a fatal error which interrupts the program flow? Is the error something internal to the method that really shouldn't have gone wrong (like assertions; I might die in that case), is it an error from an underlying layer such as a database that your code is simply passing along, or is it something the user of the method did wrong?

    Remember that there are other ways to return things from methods, such as returning multiple values, pass-by-reference parameters, setting fields on the object, etc.

    In a language like Java, where exception handling is built-in, using exceptions is a bit more common. I'd say that exception handling is not "truly built in" to Perl because for a long time eval didn't have a fully reliable $@ error message (see for example "Background" in Try::Tiny). Instead, many Perl APIs chose to return false values on failures.

    Note you may want to look into Carp instead of die, especially if the errors are something the caller of the method did wrong the error messages are more helpful.

    Regarding your third example, because of the aforementioned issues with $@, the more reliable way to use eval is: my $success = eval { $result = whatever(); 1 };

    (By the way, in your code examples, do you really mean to interpolate $object into the string?)

Re: Using die() in methods
by Anonymous Monk on Jul 06, 2014 at 20:55 UTC
    Also bear in mind that the parameter to die() can also be an object, not merely a string, so it can carry any sort of "payload" you wish, and it can be of an identifiable type (with an ancestry). Plenty of existing CPAN modules provide this easily.
Re: Using die() in methods
by tobyink (Canon) on Jul 07, 2014 at 13:47 UTC

    Generally speaking, you should die (or ExceptionClass->throw, or Carp::croak, or Carp::confess... which are all just wrappers for die) to indicate an error condition.

    Why? Because when people are using your objects like this:

    my $result = $object->do_something; if (not $result) { warn $object->last_error; }

    ... then you're heading for a situation where in order to get sensible behaviour from your object, people are needing to call multiple methods on it in a particular order, and do stuff with the results. The order in which the methods must be called should be considered one of your class' implementation details. And when one of your class' implementation details is being implemented by the caller, this goes against the principle of encapsulation — all your class' implementation details are supposed to be implemented within your class.

    Of course, you need to think about what you consider to be a true "error condition". If you're asked to look up an employee ID, and a database handle hasn't been provided, this is probably an error and you should die. If you're asked to look up an employee ID, and there is no current employee with that ID, it's less clear whether the sensible behaviour would be to die or return undef.

      Oh, yes, that's exactly why I use die() only in object-builders, because I have a habit to new() through eval(). I've got this habit, because a certain number of other people's classes sometimes could die() in the builder().

      But I think the descision to die() can have only the main module. Subroutines call other subroutine, if someone have an error occuried - okay, let them to return the special sign to the caller and the caller will report to its' caller and so on - until the process pop up to the main module. And the main module sends the long error message to the error stream: "Can't sub1(): Can't sub2(): Can't sub3(): Can't open connect to the database: ...".

      I've made a tricky Error-class for my own usage. It has the method error(), when I call it with some parameter, it stores this parameter as the error message and returns undef, so I can do return($self->error("Something bad has happened")) and the caller will get undef. When it gets the undef value, it checks for $caller->has_error(). So I can be sure whenever the method has got an error or it just doesn't find any records in the database.

      It's not worse than try/catch, but it let other people to call methods of my classes without being afraid to die().

      V.Melnik

        You wrote:

        "okay, let them to return the special sign to the caller and the caller will report to its' caller and so on - until the process pop up to the main module."

        But perhaps you might have said instead: "let them to return the special sign to the caller and the caller will report to its' caller and so on - until it reaches a level I forgot to check for the special sign."

        Exceptions are a special sign. But they are a special sign that you cannot forget to pass to your caller because Perl automatically passes it up and up and up until it finds a caller that is willing to do something useful with the special sign (i.e. catch the exception).

        So throw exceptions! Don't return special signs!

        If every level of caller needs knowledge about your "special signs", then your callers have too much knowledge about your class. Your callers have become tightly coupled to your class. This is bad.

        "But I think the descision to die() can have only the main module."

        I think you're confusing the decision to die() with the decision to exit(). I absolutely agree that the main script should be making the decision about when it exits. However, as you know, calling die() does not necessarily cause the script to exit; it can be caught by any level of caller and handled gracefully. It only causes the script to exit if an error occurs that nothing is able to handle.

        Do not fear death, you will re-awaken to a world built with Perfect Perl 7 and no Python.

        You must always remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: Using die() in methods
by perlfan (Vicar) on Jul 07, 2014 at 11:50 UTC
    Check out Exception::Class. Also, one may use the PROPAGATE method to accumulate die warnings as they may move up the call stack.

    My only advice is to be sensible and don't forget that value of clarity and consistency (if you have controle of either); I rather liberally use die() or throw (from Exception::Class) - especially for resource bound things like databases

    Testing exceptions is easy with Test::Exception.

    I've always found this Advent entry from 2011 enlightening, http://perladvent.org/2011/2011-12-17.html. I don't use try/catch, but there is good background info in there.

    As far as what value to return? Sometimes as you point out you want to return undef or 0. In cases where undef means error but 0 is a legit value, use "defined" to test the return value other than just something like if ($ret) {..}

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1092477]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (4)
As of 2024-04-24 12:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found