Feeds:
Posts
Comments

Posts Tagged ‘finally clause’

One thing that very few code samples (for any language) touch upon is error handling. There are a number of reasons for this.

  • Error handling code is ugly
  • It can take up a significant amount of the code
  • It distracts from what the sample is trying to demonstrate

Robust code almost always needs error handling. How is it done in Emacs Lisp?

Try / Catch / Finally – The Error Handling Trinity

In Java, code that may throw an exception looks like this:

try {
    // Code that may throw
} catch (SomeType exception) {
    // Handler code for SomeType
} catch (Exception exception) {
    // Handler code for Exception
} finally {
    // Code to execute after // Code + the handler if any
}

The finally clause handles the clean-up and is always executed whether or not an exception is thrown.

How Emacs Lisp Does It

Emacs Lisp has an equivalent for each part – from throwing the exception, to catching it and handling clean-up. (error ...) throws the most basic exception (in emacs lisp, they are called signals). If you execute an error call without protecting it, emacs will enter the debugger.

unwind-protect

unwind-protect is the emacs way of implementing a finally clause. In the following code, when you exit from the debugger, hi will be inserted in the buffer.

(unwind-protect
    (error "hello")
  (insert "hi"))

condition-case

Try/Catch is spelled condition-case. There is one handler per signal we want to handle. All signals are derived from ‘error so catching error catches any remaining signals.

(condition-case var bodyform &rest handlers)

(unwind-protect
    (let (retval)
      (condition-case ex
          (setq retval (error "Hello"))
        ('error (message (format "Caught exception: [%s]" ex))))
        retval)
  (message "Cleaning up..."))

We can wrap this all in a macro. Adapt the following for your own use.

(defmacro safe-wrap (fn &rest clean-up)
  `(unwind-protect
       (let (retval)
         (condition-case ex
             (setq retval (progn ,fn))
           ('error
            (message (format "Caught exception: [%s]" ex))
            (setq retval (cons 'exception (list ex)))))
         retval)
     ,@clean-up))

Some Examples

(safe-wrap (error "Hello") (message "Unwinding..."))
Caught exception: [(error Hello)]
Unwinding...
(exception (error "Hello")) ;; return value

;; (safe-wrap (message "Hello") (message "Unwinding..."))
Hello
Unwinding...
"Hello" ;; return value

(safe-wrap (/ 1 0))
Caught exception: [(arith-error)]
(exception (arith-error)) ;; return value

If you liked this post, why not subscribe to my RSS feed.

Read Full Post »

Follow

Get every new post delivered to your Inbox.