Successfully reported this slideshow.

Code Fast, die() Early, Throw Structured Exceptions

4

Share

Upcoming SlideShare
Криокомплекс
Криокомплекс
Loading in …3
×
1 of 76
1 of 76

Code Fast, die() Early, Throw Structured Exceptions

4

Share

Download to read offline

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.

More Related Content

Related Audiobooks

Free with a 14 day trial from Scribd

See all

Code Fast, die() Early, Throw Structured Exceptions

  1. 1. Live Fast, Die Young, Have A Good Looking Corpse
  2. 2. Code Fast, die() Early, Throw Structured Exceptions
  3. 3. Throw Structured Exceptions John SJ Anderson @genehack 03 Jan 2012
  4. 4. “Classic” Perl exception throwing
  5. 5. “Classic” Perl exception throwing • Throw an exception with die()
  6. 6. “Classic” Perl exception throwing • Throw an exception with die() • Or Carp::croak(), Carp::confess(), etc.
  7. 7. “Classic” Perl exception throwing • Throw an exception with die() • Or Carp::croak(), Carp::confess(), etc. • TIMTOWTDI!
  8. 8. “Classic” Perl exception throwing • Throw an exception with die() • Or Carp::croak(), Carp::confess(), etc. • TIMTOWTDI! • Catch an exception with eval {}
  9. 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. 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 }
  11. 11. Problems with “classic” Perl exceptions
  12. 12. Problems with “classic” Perl exceptions • $@ can get clobbered
  13. 13. Problems with “classic” Perl exceptions • $@ can get clobbered • $@ can get clobbered by code you don’t own
  14. 14. Problems with “classic” Perl exceptions • $@ can get clobbered • $@ can get clobbered by code you don’t own • $@ might be a false value
  15. 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. 16. Use Try::Tiny for “semi-modern” Perl exceptions
  17. 17. Use Try::Tiny for “semi-modern” Perl exceptions • Provides try{}/catch{}/finally{} blocks
  18. 18. Use Try::Tiny for “semi-modern” Perl exceptions • Provides try{}/catch{}/finally{} blocks • Handles details of properly dealing with complexities around $@
  19. 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. 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. 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. 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. 23. Problems with “semi-modern” Perl exceptions • If $@ is a string, you’re depending on duplicated information, which will break.
  24. 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. 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. 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. 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. 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. 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...)
  30. 30. So what’s the solution?
  31. 31. So what’s the solution? • die() can also take a reference as an argument
  32. 32. So what’s the solution? • die() can also take a reference as an argument • So you can die() with an object!
  33. 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. 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. 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. 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; }
  37. 37. Exception::Class Pros
  38. 38. Exception::Class Pros • Nice declarative syntax
  39. 39. Exception::Class Pros • Nice declarative syntax • Possible to declare detailed or simple exception class hierarchies very simply
  40. 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
  41. 41. Exception::Class Cons
  42. 42. Exception::Class Cons • Not really designed for use with Try::Tiny
  43. 43. Exception::Class Cons • Not really designed for use with Try::Tiny • Based on Class::Data::Inheritable, not Moose
  44. 44. A Moose role for structured exceptions: Throwable package Redirect; use Moose; with 'Throwable';   has url => (is => 'ro'); ...then later... Redirect->throw({ url => $url });
  45. 45. Throwable Pros
  46. 46. Throwable Pros • Implemented as a Moose role, so your exception classes are just normal Moose classes that consume the role
  47. 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. 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.
  49. 49. Throwable Cons
  50. 50. Throwable Cons ?
  51. 51. Throwable Cons ? So far, I haven’t really found any.
  52. 52. Throwable Cons ? So far, I haven’t really found any. (Of course, that doesn’t mean there aren’t any...)
  53. 53. Error Handling Patterns & Anti-Patterns
  54. 54. Error Handling Patterns & Anti-Patterns • DO use exceptions instead of error flags
  55. 55. Error Handling Patterns & Anti-Patterns • DO use exceptions instead of error flags • DO throw exceptions as early as possible
  56. 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. 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. 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. 59. Use exceptions instead of error flags Please please please don’t write code like this!
  60. 60. Use exceptions instead of error flags
  61. 61. Use exceptions instead of error flags • Forget just one of those checks and you’ve got a hard to track down bug
  62. 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. 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. 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 }
  65. 65. Throw exceptions as early as possible
  66. 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. 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.
  68. 68. Don’t catch exceptions except to handle them
  69. 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. 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. 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.
  72. 72. 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 }
  73. 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. 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>)
  75. 75. Thanks for your time this evening!
  76. 76. Questions?

Editor's Notes

  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • ×