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.