• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Code Fast, die() Early, Throw Structured Exceptions
 

Code Fast, die() Early, Throw Structured Exceptions

on

  • 2,610 views

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.

Statistics

Views

Total Views
2,610
Views on SlideShare
2,607
Embed Views
3

Actions

Likes
2
Downloads
14
Comments
0

2 Embeds 3

http://a0.twimg.com 2
https://duckduckgo.com 1

Accessibility

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \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

Code Fast, die() Early, Throw Structured Exceptions Code Fast, die() Early, Throw Structured Exceptions Presentation Transcript

  • Live Fast, Die Young,Have A Good Looking Corpse
  • Code Fast, die() Early,Throw Structured Exceptions
  • Throw Structured Exceptions John SJ Anderson @genehack 03 Jan 2012
  • “Classic” Perl exception throwing
  • “Classic” Perl exception throwing• Throw an exception with die()
  • “Classic” Perl exception throwing• Throw an exception with die()• Or Carp::croak(), Carp::confess(), etc.
  • “Classic” Perl exception throwing• Throw an exception with die()• Or Carp::croak(), Carp::confess(), etc.• TIMTOWTDI!
  • “Classic” Perl exception throwing• Throw an exception with die()• Or Carp::croak(), Carp::confess(), etc.• TIMTOWTDI!• Catch an exception with eval {}
  • “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 $@
  • “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 here10 }1112 sub this_might_fail {13 die "FAILED!"14 if rand() < 0.5;15 }
  • Problems with “classic” Perl exceptions
  • Problems with “classic” Perl exceptions• $@ can get clobbered
  • Problems with “classic” Perl exceptions• $@ can get clobbered• $@ can get clobbered by code you don’t own
  • Problems with “classic” Perl exceptions• $@ can get clobbered• $@ can get clobbered by code you don’t own• $@ might be a false value
  • 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.
  • Use Try::Tiny for“semi-modern” Perl exceptions
  • Use Try::Tiny for “semi-modern” Perl exceptions• Provides try{}/catch{}/finally{} blocks
  • Use Try::Tiny for “semi-modern” Perl exceptions• Provides try{}/catch{}/finally{} blocks• Handles details of properly dealing with complexities around $@
  • 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).
  • 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!
  • 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 here13 };1415 sub this_might_fail {16 die "FAILED!"17 if rand() < 0.5;18 }
  • 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.
  • Problems with “semi-modern” Perl exceptions• If $@ is a string, you’re depending on duplicated information, which will break.
  • 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 error12 }13 elsif( $_ =~ /another error/ ) {14 # handle another error15 }16 else { # not sure what the problem is, just give up17 confess( $_ );18 }19 };
  • 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 error12 }13 elsif( $_ =~ /another error/ ) {14 # handle another error15 }16 else { # not sure what the problem is, just give up17 confess( $_ );18 }19 };
  • Wait, where’s the duplicated information?• As soon as somebody “fixes” the string in some die() somewhere, you’ve potentially broken exception handling
  • 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
  • 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?
  • 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...)
  • So what’s the solution?
  • So what’s the solution?• die() can also take a reference as an argument
  • So what’s the solution?• die() can also take a reference as an argument• So you can die() with an object!
  • 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
  • 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
  • A framework for structured exceptions: Exception::Classuse Exception::Class (    MyException,     AnotherException => { isa => MyException },     YetAnotherException => {        isa         => AnotherException,        description => These exceptions are related to IPC    },     ExceptionWithFields => {        isa    => YetAnotherException,        fields => [ grandiosity, quixotic ],    },);
  • A framework for structured exceptions: Exception::Class# tryeval { MyException->throw( error => I feel funny. ) }; my $e; # catchif ( $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;}
  • Exception::Class Pros
  • Exception::Class Pros• Nice declarative syntax
  • Exception::Class Pros• Nice declarative syntax• Possible to declare detailed or simple exception class hierarchies very simply
  • 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
  • Exception::Class Cons
  • Exception::Class Cons• Not really designed for use with Try::Tiny
  • Exception::Class Cons• Not really designed for use with Try::Tiny• Based on Class::Data::Inheritable, not Moose
  • A Moose role for structured exceptions: Throwablepackage Redirect;use Moose;with Throwable; has url => (is => ro);...then later...Redirect->throw({ url => $url });
  • Throwable Pros
  • Throwable Pros• Implemented as a Moose role, so your exception classes are just normal Moose classes that consume the role
  • 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.
  • 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.
  • Throwable Cons
  • Throwable Cons ?
  • Throwable Cons ? So far, I haven’t really found any.
  • Throwable Cons ? So far, I haven’t really found any.(Of course, that doesn’t mean there aren’t any...)
  • Error HandlingPatterns & Anti-Patterns
  • Error Handling Patterns & Anti-Patterns• DO use exceptions instead of error flags
  • Error Handling Patterns & Anti-Patterns• DO use exceptions instead of error flags• DO throw exceptions as early as possible
  • 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
  • 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
  • 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();1011 my $step_two_result = $c->model(BusinessLogic)->do_step_two( $step_one_result, $session );12 $c->detach( error ) if $session->has_error();1314 $c->stash({15 one => $step_one_result ,16 two => $step_two_result ,17 });18 }
  • Use exceptions instead of error flags Please please please don’t write code like this!
  • Use exceptions instead of error flags
  • Use exceptions instead of error flags• Forget just one of those checks and you’ve got a hard to track down bug
  • 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
  • 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)
  • 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 , [ $_ ] ) };1213 $c->stash({14 one => $step_one_result ,15 two => $step_two_result ,16 });17 }
  • Throw exceptions as early as possible
  • Throw exceptions as early as possible• If you’re going to throw an exception because you didn’t get passed something, do it ASAP.
  • 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.
  • Don’t catch exceptionsexcept to handle them
  • Don’t catch exceptions except to handle them• Most of the time, your business logic code is going to throw exceptions, not catch them
  • 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 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.
  • 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 ); 910 $c->stash({11 message => $message ,12 template => error,13 });14 }
  • 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 ); 910 $c->stash({11 message => $message ,12 template => error,13 });14 } (again, not the best example ever...)
  • 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>)
  • Thanks for your time this evening!
  • Questions?