Replace Exceptions With Defaults
December 1, 2017
All programming languages provide ways to handle exceptions, and all programs should be careful to handle exceptions properly; it’s good programming practice to keep exceptions from interfering with the normal conduct of your program, and courteous to your users. A good way to handle exceptions is to replace them with default values. Consider this:
> (car '()) Exception in car: () is not a pair > (try (car '()) #f) #f
The first exception is unhandled, and displays an error to the user. The second exception is handled, and converts the error to a default value. Replacing exceptions with defaults makes programs more robust.
Your task is to devise a simple system of replacing exceptions with defaults in your favorite programming language. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
Below are my procedural and syntactic interpretations of the idea.
The syntactic version puts the default first, before the body of
expressions, because I thought that a bit nicer, though it is easy to
change.
@chaw: Your version fails because it evaluates the default expression even when it is not needed. Here is output from my macro, at the top, and your macro/procedure, at the bottom:
Both programs correctly evaluate to the
car
of the list. But your program also prints “hello” as a side-effect of evaluating the default expression, which it should not do.You can run the program at https://ideone.com/9ajhyO.
Here’s a solution in Python.
The default value is specified using a lambda. This provides deferred evaluation to address the issue that @programmingpraxis mentions in an earlier comment (so that the default is only evaluated when needed).
The function returns a second value that indicates whether the expression was evaluated successfully.
Output:
I disagree that this is in any way good practice. If you are passing a non-pair to car, it should fail immediately and loudly, as something is severely wrong. Changing it to #f just postpones the evil day and makes the error harder to debug.
That said, long-running programs do need to catch exceptions so as to remain long-running, and the Nulll Object pattern (which returns an object of the correct type with no contents rather than a generic nil) is perfectly good even in Lisps.
@John: I used (car ‘()) as an example. I would not handle type errors that way in practice. The text makes clear that this is a mechanism for handling some types of exceptions, not as a way to bypass real errors. For instance, if a binary tree lookup doesn’t find an expected key, this mechanism can replace the exception with some default value so the program can continue.