7. “Classic” Perl exceptions
• Throw an exception with die()
• Or Carp::croak(), Carp::confess(), etc.
• TIMTOWTDI!
8. “Classic” Perl exceptions
• Throw an exception with die()
• Or Carp::croak(), Carp::confess(), etc.
• TIMTOWTDI!
• Catch an exception with eval {}
9. “Classic” Perl exceptions
• Throw an exception with die()
• Or Carp::croak(), Carp::confess(), etc.
• TIMTOWTDI!
• Catch an exception with eval {}
• Handle an exception by looking at $@
10. “Classic” Perl exception throwing
1 #! /usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 eval { my $result = this_might_fail() };
7
8 if( $@ ) {
9 # handle the error here
10 }
13. Problems with “classic” Perl exceptions
• $@ can get clobbered
• $@ can get clobbered by code you don’t own
14. Problems with “classic” Perl exceptions
• $@ can get clobbered
• $@ can get clobbered by code you don’t own
• $@ might be a false value
15. Problems with “classic” Perl exceptions
• $@ can get clobbered
• $@ can get clobbered by code you don’t own
• $@ might be a false value
• If $@ is a string, you’re depending on duplicated
information, which will break.
17. Use Try::Tiny for
“semi-modern” Perl exceptions
• Provides try{}/catch{}/finally{} blocks
18. Use Try::Tiny for
“semi-modern” Perl exceptions
• Provides try{}/catch{}/finally{} blocks
• Handles details of properly dealing with complexities
around $@
19. Use Try::Tiny for
“semi-modern” Perl exceptions
• Provides try{}/catch{}/finally{} blocks
• Handles details of properly dealing with complexities
around $@
• Lightweight and generally Just Works(tm).
20. Use Try::Tiny for
“semi-modern” Perl exceptions
• Provides try{}/catch{}/finally{} blocks
• Handles details of properly dealing with complexities
around $@
• Lightweight and generally Just Works(tm).
• N.b.: you have to end try{}/catch{} with a
semicolon. Don’t forget this!
21. Use Try::Tiny for
“semi-modern” Perl exceptions
1 #! /usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 use Try::Tiny;
7
8 try {
9 my $result = this_might_fail();
10 }
11 catch {
12 # handle the error here
13 };
22. Problems with
“semi-modern” Perl exceptions
• $@ can get clobbered
• $@ can get clobbered by code you don’t own
• $@ might be a false value
• If $@ is a string, you’re depending on duplicated
information, which will break.
23. Problems with
“semi-modern” Perl exceptions
• If $@ is a string, you’re depending on duplicated
information, which will break.
24. Wait, where’s the duplicated information?
1 my $answer;
2 try {
3 # any of these might throw an exception
4 my $this = this_might_fail();
5 my $that = something_else_might_fail();
6 $answer = combine_them( $this , $that );
7 }
8 catch {
9 # our error is in $_
10 if( $_ =~ /some error/ ) {
11 # handle some error
12 }
13 elsif( $_ =~ /another error/ ) {
14 # handle another error
15 }
16 else { # not sure what the problem is, just give up
17 confess( $_ );
18 }
19 };
25. Wait, where’s the duplicated information?
1 my $answer;
2 try {
3 # any of these might throw an exception
4 my $this = this_might_fail();
5 my $that = something_else_might_fail();
6 $answer = combine_them( $this , $that );
7 }
8 catch {
9 # our error is in $_
10 if( $_ =~ /some error/ ) {
11 # handle some error
12 }
13 elsif( $_ =~ /another error/ ) {
14 # handle another error
15 }
16 else { # not sure what the problem is, just give up
17 confess( $_ );
18 }
19 };
26. Wait, where’s the duplicated information?
• As soon as somebody “fixes” the string in some die()
somewhere, you’ve potentially broken exception
handling
27. Wait, where’s the duplicated information?
• As soon as somebody “fixes” the string in some die()
somewhere, you’ve potentially broken exception
handling
• And you can’t even easily tell where, because it’s
probably in a regexp that doesn’t look at all like the
changed string
28. Wait, where’s the duplicated information?
• As soon as somebody “fixes” the string in some die()
somewhere, you’ve potentially broken exception
handling
• And you can’t even easily tell where, because it’s
probably in a regexp that doesn’t look at all like the
changed string
• Even if you have tests for the code in question, do you
really have test coverage on all your exception cases?
30. So what’s the solution?
• die() can also take a reference as an argument
31. So what’s the solution?
• die() can also take a reference as an argument
• So you can die() with an object!
32. So what’s the solution?
• die() can also take a reference as an argument
• So you can die() with an object!
• Which means you can cram all sorts of useful information into your
exceptions
33. So what’s the solution?
• die() can also take a reference as an argument
• So you can die() with an object!
• Which means you can cram all sorts of useful information into your
exceptions
• And more importantly, handle them in a structured fashion that’s much less
brittle than string comparisons
34. A framework for structured exceptions:
Exception::Class
use Exception::Class (
'MyException',
'AnotherException' => { isa => 'MyException' },
'YetAnotherException' => {
isa => 'AnotherException',
description => 'These exceptions are related to IPC'
},
'ExceptionWithFields' => {
isa => 'YetAnotherException',
fields => [ 'grandiosity', 'quixotic' ],
},
);
38. Exception::Class Pros
• Nice declarative syntax
• Possible to declare detailed or simple exception class
hierarchies very simply
39. Exception::Class Pros
• Nice declarative syntax
• Possible to declare detailed or simple exception class
hierarchies very simply
• Supports aliasing to make throwing particular exception
types easier
42. Exception::Class Cons
• Not really designed for use with Try::Tiny
• Based on Class::Data::Inheritable, not Moose
43. A Moose role for structured exceptions:
Throwable
package Redirect;
use Moose;
with 'Throwable';
has url => (is => 'ro');
...then later...
Redirect->throw({ url => $url });
45. Throwable Pros
• Implemented as a Moose role, so your exception
classes are just normal Moose classes that consume
the role
46. Throwable Pros
• Implemented as a Moose role, so your exception
classes are just normal Moose classes that consume
the role
• So you get the usual Moose-y good stuff around
attributes and methods and such.
47. Throwable Pros
• Implemented as a Moose role, so your exception
classes are just normal Moose classes that consume
the role
• So you get the usual Moose-y good stuff around
attributes and methods and such.
• Comes with a grab-bag of typical exception behaviors
(in Throwable::X), like stack traces, printf-ish
messages, etc.
51. Throwable Cons
?
So far, I haven’t really found any.
(Of course, that doesn’t mean there aren’t any...)
52. Error Handling
Patterns & Anti-Patterns
• DO use exceptions instead of error flags
• DO throw exceptions as early as possible
• DON’T catch exceptions unless you’re going to handle
them – just let them propagate upwards
• DO design your web application-level error actions to
handle your business logic-level exceptions
53. Use exceptions instead of error flags
1 sub some_catalyst_action :Local {
2 my( $self , $c ) = @_;
3 my $session = get_huge_session_object( $c->session );
4
5 my $validated_params = validate_request_params( $c->request->params )
6 or $c->detach( 'error' );
7
8 my $step_one_result = $c->model('BusinessLogic')->do_step_one( $session , $validated_params );
9 $c->detach( 'error' ) if $session->has_error();
10
11 my $step_two_result = $c->model('BusinessLogic')->do_step_two( $step_one_result, $session );
12 $c->detach( 'error' ) if $session->has_error();
13 }
56. Use exceptions instead of error flags
• Forget just one of those checks and you’ve got a hard to track
down bug
57. Use exceptions instead of error flags
• Forget just one of those checks and you’ve got a hard to track
down bug
• $session is being passed just to provide access to that error
flag.
58. Use exceptions instead of error flags
• Forget just one of those checks and you’ve got a hard to track
down bug
• $session is being passed just to provide access to that error
flag.
• The error action gets no real info about what the problem was,
or it tries to pull it from $session itself (which has its own
problems)
61. Throw exceptions as early as
possible
• If you’re going to throw an exception because you
didn’t get passed something, do it ASAP.
62. Throw exceptions as early as
possible
• If you’re going to throw an exception because you
didn’t get passed something, do it ASAP.
• In general, the quicker you can die(), the better –
because it reduces the amount of code that might
contain the bug.
64. Don’t catch exceptions
except to handle them
• Most of the time, your business logic code is going to
throw exceptions, not catch them
65. Don’t catch exceptions
except to handle them
• Most of the time, your business logic code is going to
throw exceptions, not catch them
• If you do catch an exception, you should be trying to fix
the problem.
66. Don’t catch exceptions
except to handle them
• Most of the time, your business logic code is going to
throw exceptions, not catch them
• If you do catch an exception, you should be trying to fix
the problem.
• Don’t catch exceptions just to munge them or log them
and re-throw them. Munge them or log them before
you throw them.
67. Further reading
• Throwable::X: common behavior for thrown exceptions
(<http://rjbs.manxome.org/rubric/entry/1860>)
• Exceptionally Extensible Exceptions
(<http://advent.rjbs.manxome.org/2010/2010-12-03.html>)
• Structured Data and Knowing versus Guessing
(<http://www.modernperlbooks.com/mt/2010/10/structured-data-and-knowing-versus-guessing.html>)
69. Further reading
• Throwable::X: common behavior for thrown exceptions
(<http://rjbs.manxome.org/rubric/entry/1860>)
• Exceptionally Extensible Exceptions
(<http://advent.rjbs.manxome.org/2010/2010-12-03.html>)
• Structured Data and Knowing versus Guessing
(<http://www.modernperlbooks.com/mt/2010/10/structured-data-and-knowing-versus-guessing.html>)