10.12.3. Discussion
Raising exceptions is not a facility to be used lightly. Most
functions should return an error using a bare
return statement. Wrapping every call in an
exception trap is tedious and unsightly, removing the appeal of using
exceptions in the first place.
But, on rare occasions, failure in a function should cause the entire
program to abort. Rather than calling the irrecoverable
exit function, you should call
die instead, which at least gives the programmer
the chance to cope. If no exception handler has been installed via
eval, then the program aborts at that point.
To detect this, wrap the call to the function with a block
eval. The $@ variable will be
set to the offending exception if one occurred; otherwise, it will be
false.
eval { $val = func( ) };
warn "func blew up: $@" if $@;
Any eval catches all exceptions, not just specific
ones. Usually you should propagate unexpected exceptions to an
enclosing handler. For example, suppose your function raised an
exception containing the string "Full
moon!". You could safely trap that exception while
letting others through by inspecting the $@
variable. Calling die without an argument uses the
contents of $@ to construct a new exception
string.
eval { $val = func( ) };
if ($@ && $@ !~ /Full moon!/) {
die; # re-raise unknown errors
}
If the function is part of a module, consider using the Carp module
and call croak or confess
instead of die. The only difference between
die and croak is that with
croak, the error appears to be from the caller's
perspective, not the module's. The confess
function, on the other hand, creates a full stack backtrace of who
called whom and with what arguments.
Another intriguing possibility is for the function to detect that its
return value is being completely ignored because the function was
called in a void context. If that were returning an error indication
would be useless, so raise an exception instead.
if (defined wantarray( )) {
return;
} else {
die "pay attention to my error!";
}
Of course, just because it's not voided doesn't mean the return value
is being dealt with appropriately. But if it is voided, it's
certainly not being checked.
There are CPAN modules that offer alternative ways of handling
exceptions. The Error module offers try,
catch, and throw notation
instead of eval and die:
use Error ':try';
try {
something( );
}
catch Error::Database with {
my $e = shift;
warn "Problem in " . $e->{'-database'} . " (caught)\n";
};
Error offers try, catch ...
with, except,
otherwise, and finally blocks
for maximum flexibility in error handling. The Exception::Class
module from CPAN lets you create classes of exceptions and objects to
represent specific exceptions. The two modules can be combined so
that you can catch these exception objects.