Why I like throwing exceptions

I program a lot in Perl. I used to be a big fan of “modules should never die”. Now I’m the opposite. I want a method to die any time it can’t succeed in doing what it’s been asked to do.

Here’s why:

[ad#Adsense]

Without exceptions:

$module->do_something( %args ) or die "I couldn't do something";
$module->do_something_else( %args ) or die "I couldn't do something else";
[...]

With exceptions:

$module->do_something( %args );
$module->do_something_else( %args );

Now imagine that with hundreds of lines of code. I love having my script or module simply contain a list of commands – Do this then this then this – without having to babysit every single line to make sure everything went ok.

It also lets you handle things on a more transactional basis. Say you want to connect to a database and update some data. With exceptions you can do this:

use Error.pm qw(:try);

my $dbh = DBI->connect($data_source, $username, $auth, { RaiseError => 1, AutoCommit => 0 } );
# RaiseError turned on exceptions. We’re free to roam.

try {
$dbh->do(“update mytable set myfield=’gobble’ where myotherfield=’turkey'”);
$dbh->do(“update anothertable set anotherfield=’somevalue’ where yetanother=’anothervalue'”);
$dbh->commit;
} otherwise {
$dbh->revert;
};

(Don’t quote me on syntax here – this is very perl-like psuedocode. 🙂 )
[ad#Adsense]
I also like building exceptions into my own modules using Error.pm. This lets me throw specific types of exceptions. I usually end up with an exception type for data errors, IO errors, and Database errors, for when I want to catch those. For example, I may wait and retry IO and Database errors, but a Data error (bad user data) I’ll let go un-caught up to the UI level so that the UI can complain to the user.

That brings me to the other reason I like exceptions, and the reason I started using them in the first place. Say you’re building a complex module, or set of modules, with many layers of subroutine calls. If your error-trapping mechanism is the ever-popular “return undef”, you now have, say, 5 layers of subroutine/method calls to check and pass undef up. You forget an “or return undef” at any level, and you let an error slip by. Usually something’ll break later in your program as a result, and you’ll have a hard-to-track bug. I prefer to throw an exception and include a stack trace (see the Error.pm docs for sample code). Then you can catch the exception at any point, and if you don’t, you still have a stack trace that shows exactly where the error occurred.

“But I don’t want my script killed by a module I’m using, I want to decide what to do if there’s a problem”. I used to think this. But the fact is, 99.9% of the time you’re going to do one of two things:

  1. $module->do_something() or die “Couldn’t do something: ” . $module->errstr;
  2. $module->do_something();

In the first case, if do_something threw an exception, it’d save you the babysitting code. In the second case, if “do_something” didn’t do what it was supposed to do, chances are your program can’t really continue anyway, right? And if it can continue, maybe you don’t need to “do_something()” anyway. And for that .1% of the time you want to ignore errors, you can “eval { $module->do_something(); }”.

So there. I like throwing exceptions. Maybe you will too.

Further reading: