SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.
SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.
Successfully reported this slideshow.
Activate your 14 day free trial to unlock unlimited reading.
Slides from a short talk given at January 2012 DC.pm. Covers "classic" exceptions in Perl as well as some libraries to make working with exceptions easier.
Slides from a short talk given at January 2012 DC.pm. Covers "classic" exceptions in Perl as well as some libraries to make working with exceptions easier.
5.
“Classic” Perl exception throwing
• Throw an exception with die()
6.
“Classic” Perl exception throwing
• Throw an exception with die()
• Or Carp::croak(), Carp::confess(), etc.
7.
“Classic” Perl exception throwing
• Throw an exception with die()
• Or Carp::croak(), Carp::confess(), etc.
• TIMTOWTDI!
8.
“Classic” Perl exception throwing
• Throw an exception with die()
• Or Carp::croak(), Carp::confess(), etc.
• TIMTOWTDI!
• Catch an exception with eval {}
9.
“Classic” Perl exception throwing
• 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 }
11
12 sub this_might_fail {
13 die "FAILED!"
14 if rand() < 0.5;
15 }
12.
Problems with “classic” Perl exceptions
• $@ can get clobbered
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.
16.
Use Try::Tiny for
“semi-modern” Perl exceptions
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 };
14
15 sub this_might_fail {
16 die "FAILED!"
17 if rand() < 0.5;
18 }
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?
• Even if you have tests for the code in question, do you
really have test coverage on all your exception cases?
29.
Wait, where’s the duplicated information?
• Even if you have tests for the code in question, do you
really have test coverage on all your exception cases?
• (Almost certainly not. If you do, come write tests for
$WORK_PROJECT, we need the help...)
31.
So what’s the solution?
• die() can also take a reference as an argument
32.
So what’s the solution?
• die() can also take a reference as an argument
• So you can die() with an object!
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
34.
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
35.
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' ],
},
);
39.
Exception::Class Pros
• Nice declarative syntax
• Possible to declare detailed or simple exception class
hierarchies very simply
40.
Exception::Class Pros
• Nice declarative syntax
• Possible to declare detailed or simple exception class
hierarchies very simply
• Supports macro definitions to make throwing particular
exception types easier
42.
Exception::Class Cons
• Not really designed for use with Try::Tiny
43.
Exception::Class Cons
• Not really designed for use with Try::Tiny
• Based on Class::Data::Inheritable, not Moose
44.
A Moose role for structured exceptions:
Throwable
package Redirect;
use Moose;
with 'Throwable';
has url => (is => 'ro');
...then later...
Redirect->throw({ url => $url });
46.
Throwable Pros
• Implemented as a Moose role, so your exception
classes are just normal Moose classes that consume
the role
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.
48.
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.
54.
Error Handling
Patterns & Anti-Patterns
• DO use exceptions instead of error flags
55.
Error Handling
Patterns & Anti-Patterns
• DO use exceptions instead of error flags
• DO throw exceptions as early as possible
56.
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
57.
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
58.
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
14 $c->stash({
15 one => $step_one_result ,
16 two => $step_two_result ,
17 });
18 }
59.
Use exceptions instead of error flags
Please please please don’t write code like this!
61.
Use exceptions instead of error flags
• Forget just one of those checks and you’ve got a hard to track down
bug
62.
Use exceptions instead of error flags
• Forget just one of those checks and you’ve got a hard to track down
bug
• Many times, $session was passed just to provide access to that
error flag. Far too much information was being passed around for
no reason
63.
Use exceptions instead of error flags
• Forget just one of those checks and you’ve got a hard to track down
bug
• Many times, $session was passed just to provide access to that
error flag. Far too much information was being passed around for
no reason
• 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)
64.
Use exceptions instead of error flags
1 sub some_catalyst_action :Local {
2 my( $self , $c ) = @_;
3
4 try {
5 my $validated_params = validate_request_params( $c->request->params )
6
7 my $step_one_result = $c->model('BusinessLogic')->do_step_one( $session , $validated_params );
8
9 my $step_two_result = $c->model('BusinessLogic')->do_step_two( $step_one_result, $session );
10 }
11 catch { $c->detach( 'error' , [ $_ ] ) };
12
13 $c->stash({
14 one => $step_one_result ,
15 two => $step_two_result ,
16 });
17 }
66.
Throw exceptions as early as
possible
• If you’re going to throw an exception because you
didn’t get passed something, do it ASAP.
67.
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.
69.
Don’t catch exceptions
except to handle them
• Most of the time, your business logic code is going to
throw exceptions, not catch them
70.
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.
71.
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.
73.
Web application error actions
should handle exceptions
1 sub error :Private {
2 my( $self , $c , $error ) = @_;
3
4 my $message = 'An unexpected error happened.';
5
6 # NOTE: duck typing
7 $message = $error->user_visible_message
8 if( $error->has_user_visible_message and ! $error->is_private );
9
10 $c->stash({
11 message => $message ,
12 template => 'error',
13 });
14 }
(again, not the best example ever...)
74.
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>)