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.
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 }
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 };
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 âďŹxesâ the string in some die()
somewhere, youâve potentially broken exception
handling
27. Wait, whereâs the duplicated information?
⢠As soon as somebody âďŹxesâ 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' ],
    },
);
36. A framework for structured exceptions:
Exception::Class
# try
eval { MyException->throw( error => 'I feel funny.' ) };
Â
my $e;Â
# catch
if ( $e = Exception::Class->caught('MyException') ) {
    warn $e->error, "n", $e->trace->as_string, "n";
    warn join ' ', $e->euid, $e->egid, $e->uid, $e->gid, $e->pid;
    exit;
}
elsif ( $e = Exception::Class->caught('ExceptionWithFields') ) {
    $e->quixotic ? do_something_wacky() : do_something_sane();
}
else {
    $e = Exception::Class->caught();
    ref $e ? $e->rethrow : die $e;
}
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 deďŹnitions to make throwing particular
exception types easier
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 ďŹags
55. Error Handling
Patterns & Anti-Patterns
⢠DO use exceptions instead of error ďŹags
⢠DO throw exceptions as early as possible
56. Error Handling
Patterns & Anti-Patterns
⢠DO use exceptions instead of error ďŹags
⢠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 ďŹags
⢠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 ďŹags
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 ďŹags
Please please please donât write code like this!
61. Use exceptions instead of error ďŹags
⢠Forget just one of those checks and youâve got a hard to track down
bug
62. Use exceptions instead of error ďŹags
⢠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 ďŹag. Far too much information was being passed around for
no reason
63. Use exceptions instead of error ďŹags
⢠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 ďŹag. 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 ďŹags
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 ďŹx
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 ďŹx
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>)