Programming Praxis


Home | Pages | Archives


Replace Exceptions With Defaults

December 1, 2017 10:00 AM

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.

Posted by programmingpraxis

Categories: Exercises

Tags:

5 Responses to “Replace Exceptions With Defaults”

  1. 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.

    (import (scheme base)
            (scheme write))
    
    (define (make-try/default proc dflt)
      (lambda args
        (guard (exc (else dflt))
          (apply proc args))))
    
    (define-syntax try/default
      (syntax-rules ()
        ((_  dflt body body1 ...)
         ((make-try/default (lambda () body body1 ...) dflt)))))
    
    (define (test)
      (define car/default (make-try/default car 42))
      (display (map car/default '((2 3 5) (2) ())))
      (newline)
      (display (try/default '(4 2 42)
                 (define lst '(2 3 5))
                 (map car (list lst '(2) '()))))
      (newline))
    
    (test)
    

    By chaw on December 1, 2017 at 3:43 PM

  2. @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:

    > (try (car '(2)) (begin (display "hello") (newline) 3))
    2
    > (try/default (begin (display "hello") (newline) 3) (car '(2)))
    hello
    2

    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.

    By programmingpraxis on December 1, 2017 at 7:48 PM

  3. 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.

    def trydefault(expr, default):
        try:
            output = expr()
            success = True
        except:
            output = default()
            success = False
        return output, success
    
    l = []
    element, _ = trydefault(lambda: l[0], lambda: None)
    print element
    

    Output:

    None
    

    By Daniel on December 2, 2017 at 6:04 PM

  4. 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.

    By John Cowan on December 12, 2017 at 12:26 PM

  5. @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.

    By programmingpraxis on December 12, 2017 at 1:48 PM

Leave a Reply



Mobile Site | Full Site


Get a free blog at WordPress.com Theme: WordPress Mobile Edition by Alex King.