http://qs1969.pair.com?node_id=1102209

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

Dear colleagues,

I think, it's a matter of religion, but I'd like to get to know more on how other people, more experienced, prefer to handle errors/exceptions in respect to the structure of your programs.

Let me describe how I'm doing it now and, if you have some time to share your experience, I'd be very grateful to you for describing how do you prefer to do it.

My own "rules" for myself are quite simple.

  1. Don't die() while executing a subrotine or method. Only the main module can die() if something goes wrong. Nobody can predict where the class will be used, so an unexpected die() can break the caller's logic.
  2. If I've got an exception inside of a subroutine, the subroutine may return(undef). If everything's fine, it return's some value (it can be true or false - no matter), but if some error has been occuried (e.g. if we can't get data from the database), the undef shall be returned.

That's okay, but how to let the caller know what's happened with the subroutine? As I think, the caller must have some explaination to be able to write something to the log-file or to show the error message to the operator. So, there is one more rule.

  1. Any class may have the "errstr" attribute, so if its' methor returned undef, the caller may get the explaination from this attribute.

So, usually it looks like this:

package SomeClass; #... sub some_method { # ... eval { die("Oops!"); }; if($@) { $self->{'errstr'} = "Something has gone wrong: $@"; return(undef) } # ... } #... package main; #... my $result = $obj->some_method; unless(defined($result)) { die("Can't SomeClass->some_method(): $obj->{'errstr'}"); } #...

And, when something goes wrong, I can get something like that:

Can't SomeClass->some_method(): Can't AnotherClass->another_method(): Can't OtherClass->other_method(): Can't open(): No such file at script.pl line 666.

Frankly speaking, I have a persistent feeling that there are some other, much more elegant way to do it. And I hate how the final error message looks like. Just like "can't A, because can't B, because can't C, because f*** you". Ugh... :(

And there is another annoying thing: I have to use die() in the constructor of an object (I mean new()), because if the constructor returns undef, the caller doesn't have an access to the object's "errstr" attribute at all (as we don't have the object bless()ed). So I have to always call constructors from eval()-blocks and get the explaination from $@.

package SomeClass; #... sub new { # ... eval { die("Oops!"); }; if($@) { die("Something has gone wrong: $@"); } # ... } #... package main; #... my $obj = eval { $obj->new }; unless(defined($obj)) { die("Can't SomeClass->new(): $@"); } #...

I absolutely hate it, but I don't see better ways to let the caller know why the object hasn't been blessed.

Maybe I should consider using some global variable to keep the reference to the stack of errors occuried? What do you think?

Maybe I should always die() (or confess() - as a better way to get to know who has called whom) inside of any method and call every method inside of eval()- or try()-block?

I'll be very grateful to each of you for sharing your best practices on this matter. It really makes me feel unsatisfacted. :)

Have a nice time!

UPD: Now I see, why it's better to use exceptions such as die(), croak() or even my own exception classes based on Throwable::Error superclass instead of returning undef's or setting flags. Lots of thanks to all!

V.Melnik