Thursday, April 08, 2010

Are Exceptions so Sacred? My Disagreement with the Pragmatic Programmers.

For the past decade I've tried to grapple with the best way to use Exceptions in programming. I've personally been moving away from using exceptions, based on Hunt and Thomas's advice in "The Pragmatic Programmer".


One of the problems with exceptions is knowing when to use them. We believe that exceptions should rarely be used as part of a program's normal flow; exceptions should be reserved for unexpected events. Assume that an uncaught exception will terminate your program and ask yourself, "Will this code still run if I remove all the exception handling?" If the answer is "no," then maybe exceptions are being used in nonexceptional circumstances.

or example, if your code tries to open a file for reading and that file does not exist, should an exception be raised?

Our answer is, "It depends." If the file should have been there, then an exception is warranted. Something unexpected happened--a file you were expecting to exist seems to have disappeared. On the other hand, if you have no idea whether the file should exist or not, then it doesn't seem exceptional if you can't find it, and an error return is appropriate.


They go on to distinguish between opening /etc/passwd on a Unix system, versus opening a file specified by a user:

public boolean open_user_file(String name) throws FileNotFoundException {
File f = new File(name);
if (!f.exists()) {
return false;
}
ipstream = new FileInputStream(f);
return true;
}


Andrew Hunt and Dave Thomas: You're just wrong! Here's the problem: a method can really only return one value, or it can throw one exception. If the return value has to be used to return an indication of success, then it can't be used to return something functional.

Look at their "good" example in open_user_file -- where does ipstream come from? It just comes out of nowhere -- out of global scope somewhere. This was their trick to let the function affect two values on its return -- both the return value (boolean) to show whether success had occurred, and also the ipstream.

If they hadn't used the global scope like this, they'd have had to store one of those two in a parameterized value, which would have been awkward.

All this to avoid using Exceptions!

It's just silliness, and it works against the intention of the Java creators. They wanted to keep all the silly checking for oddball stuff out of the main stream of code:


readFile {
try {
open the file;
determine its size;
allocate that much memory;
read the file into memory;
close the file;
} catch (fileOpenFailed) {
doSomething;
} catch (sizeDeterminationFailed) {
doSomething;
} catch (memoryAllocationFailed) {
doSomething;
} catch (readFailed) {
doSomething;
} catch (fileCloseFailed) {
doSomething;
}
}


Did you catch that? Inside the try block you'll see the Sunny-Day Scenario. Let's just assume everything goes right -- this is how it should work. It's the main course of logic.

No comments: