Basic exception handling in Perl
Basic try catch pattern in perlhttp://www.perlfoundation.org/perl5/index.cgi?exception_handling
[copy-paste]
The most basic way to handle exceptions in Perl is to use an eval BLOCK and have $@ transmit the error.
Because the eval block ends with a true statement, it will always return true if it succeeds and false otherwise.
$@ can be a simple string, reference or an object, and is "thrown" by die
This method is essentially non-atomic and has proven finicky. The problem lies with using $@. As it is global and there are many things which can reset it, either intentionally or otherwise, between the eval block failing and when it is checked.
=====eval { # try ...run the code here.... 1; } or do { # catch ...handle the error using $@... };
Because the eval block ends with a true statement, it will always return true if it succeeds and false otherwise.
$@ can be a simple string, reference or an object, and is "thrown" by die
Dangers of using $@
The more common, but subtly flawed, idiom is to check $@ to see if the eval failed.# FLAWED! eval { # try ...run the code here... }; if( $@ ) { # catch ...handle the error using $@... }
This method is essentially non-atomic and has proven finicky. The problem lies with using $@. As it is global and there are many things which can reset it, either intentionally or otherwise, between the eval block failing and when it is checked.
Other blog entry about error handling
http://perliscope.blogspot.com/2009/11/perl-error-handling.html
=====
[update]
CPAN modules
Try::Tiny # simple and functinal
TryCatch # has more dependencies but is nicer than Try::Tiny
Error
Exception::Class
Other modules are useful for checking exception handling during testing:
========
== EDITED 2010-10-15==
See new discusion in perlmonks
See new discusion in perlmonks
Best Practices for Exception Handling
- Re^2: Best Practices for Exception Handling by adrianh (Chancellor) on Jan 29, 2003 at 17:21 UTC
- Robustness. I can forget to check for an returned error value. I cannot forget to check for an exception.
- Brevity. I prefer:
to$o->foo->bar->fribble->ni
$o->foo or return(ERROR_FOO); $o->bar or return(ERROR_BAR); $o->fribble or return(ERROR_FRIBBLE); $o->ni or return(ERROR_NI); - Clarity. With exception based code the "normal" flow of control is more explicit because it is not obscured by error handling code. I think that the first of the two code examples above shows the intent of the code more directly than the second does.
- Separation of concerns. The error condition and the error handler are different ideas.
- You may want an error to be handled in different ways depending on the context.
- You may also not know how the error should be handled at the point it occurs.
- You may not know how the error should be handled at the time you write the code.
- propogate error conditions to where the decision on how they should be handled can be made.
- propogating error handlers down to where the errors may occur
- No confusion between return values and error conditions.
- Some reasons I like exceptions:
Re: Re: Best Practices for Exception Handling
by IlyaM (Parson) on Jan 30, 2003 at 09:54 UTC The fact is that when you write code in modular fashion one part of your system cannot always know how to handle errors itself. In such cases the only thing you can do is pass error somewhere else and there are in general two ways to do it: exceptions and return codes. And exceptions is just a more robust way to do it.
Example: say you are implementing business logic for your application which has multiple frontends (CLI, web and GUI). This part of your application encounters an error (let say a database connection error). What should it do? Print HTML page with error? Produce plain text formated error message for CLI? Write something in the log? No, it is not responsiblity of this part of your system to do these things, it is responsiblity of the frontend part to handle this error. So you just raise an exception and let the frontend to handle it.
- Re: Re: Best Practices for Exception Handling by IlyaM (Parson) on Feb 03, 2003 at 22:26 UTC
Do you agree that the logic responsible for asking users for a better input belongs to the user interface part and it is not a part of the business logic? I.e. business logic layer have to callback user interface part when it tries to do the error recovery. So you still have to pass control to the callee to do the error recovery that business logic part cannot do on its own as if you were using exception or return codes style of error handling. To me it looks that the error recovery mechanism is essantially the same in both cases. The only difference is added complexity of callback approach. Let's plot a couple of diagrams. Traditional approach (exceptions or return codes) for case when the business logic part bails with error:
If at the last point the user interface part can handle the error it can either ask the business logic part to redo the action or if this error is unrecoverable print diagnostic error or do something else. Now callback approach:UserInterface BusinessLogic | user input | |------------------------------>| | do some action | |<----------failure-------------| | handle the error | |
This diagram clearly show two problems:UserInterface BusinessLogic UserInte +rface | user input | |------------------------------>| | do some action | |----------failure------------->| | handle th +e error | |
- This design adds additional requirement on reentrability for at least the user interface and probably for other parts of the system if the error recovery callback calls them.
- If error recovery callback can handle the error then program flow is clear. It returns the control to the business logic part which in its turn returns the control to the callee, i.e. back to the user interface part. But what about another case when it cannot handle the error? You still have to return the control back using either exceptions or return codes! Why then bother with callbacks at all?
- Re: Re^2: Best Practices for Exception Handling by demerphq (Chancellor) on Jan 29, 2003 at 17:06 UTC
Personally I think the following little trick/modification makes for cleaner code... (and I think that raise_error makes more sense if it contains the error message returned...)
If you can contrive to make raise_error() return undef you can make it even cleanersub bar { my( $self, @args )= @_; eval { $self->method( @args ); 1} or $self->raise_error( $@, @_ ) and return undef; }
sub bar { my( $self, @args )= @_; eval { $self->method( @args ); 1} or return $self->raise_error( $@, @_ );}
6 comments:
This is interesting - but if we need to assume that $@ can contain something else then the error we are catching - then we also need to abstain from checking $@ in the
} or do { # catch
...handle the error using $@...
};
fragment.
Note that AFAICT this blog is just a bunch of copy-paste from popular Perl sites.
Please see the Try::Tiny and TryCatch modules. This is a solved problem.
[to anonymous] yes, many of the entries about perl here are copy paste from popular Perl sites. You need to blame google and the closing of google-notebook ;-). So I am keeping here some interesting notes for myself, and puting the original link I giving to the source more google points ;-).
[to zby] I think that $@ in the catch block will contain the right error message. This is the reason of recommending atomic try/catch with the 'or' in order to prevent $@ gets changed by other 'issues'. The point here is to discourage doing the eval{} and tenting $@ separately.
[to nperez] I wanted to stress that eval{}......if($@) is not a very good practice and that eval{}or do {} would be a better practice. But you are right and I should add some CPAN modules that deal with this like
TryCatch # has more dependencies but is nicer than Try::Tiny
Error
Exception::Class
Post a Comment