Detour on a common lisp nag...exception handling

A few notes on exception handling in common lisp. It is different than just plain old termination-based models of exception handling. Does that make it better? Perhaps in some situations, yes. In many situations, perhaps. You can also build your own exception handling protocols using the general signal capabilities in common lisp. While using handle-bind may make your code less referentially transparent, does that matter in all situations? Clearly, you should avoid using exceptions for flow control in your program, which python and java seem to almost support, but what's the best model for any specific application? If one were to use a function approach to providing "handlers", you would be adding alot of arguments and syntax to your function calls over time and that makes it harder as well.
No clear, one-size-fits-all answer.
;;; Demonstration of simply try-catch logic in CL
;;; as well as restart logic.

                    ; Define exception objects, subclasses of error class.
(define-condition bad-xml (error)
  ((text :initarg :text :reader text)))

(define-condition short-message (error)
  ((text :initarg :text :reader text))) 

(define-condition unknown-xml (error)
  ((text :initarg :text :reader text)))

;;; Standard try-catch logic.
(handler-case
    (let ((input-xml ""))
      (error 'short-message :text input-xml))
  (bad-xml (ex) (format nil "Bad input! ~A" ex))
  (short-message (ex) (format nil "Short message! ~A" ex)))

;;; Standard try-catch logic that jumps you to the debugger.
(handler-case
    (let ((input-xml ""))
      (error 'unknown-xml :text input-xml))
  (bad-xml (ex) (format nil "Bad input! ~A" ex))
  (short-message (ex) (format nil "Short message! ~A" ex)))


;;; Higher level handler decides what to do. Lower-level
;;; function has to list the restarts that are possible.
(handler-bind ((unknown-xml
        #'(lambda (c)
            (invoke-restart 'return-nil)))
           (bad-xml
        #'(lambda (c)
            (invoke-restart 'return-something-else ""))))
  (restart-case
      (let ((input-xml ""))
    (error 'bad-xml :text input-xml))
    (return-nil () nil)
    (return-something-else (something) something)))


;;; Handler restart is factored out using CL convention.
(defun invoke-return-nil-handler (c)
  (let ((restart (find-restart 'return-nil)))
    (when restart (invoke-restart restart))))

(defun invoke-return-something-else-handler (c)
  (let ((restart (find-restart 'use-value)))
    (when restart (invoke-restart restart ""))))

(handler-bind ((unknown-xml #'invoke-return-nil-handler)
           (bad-xml #'invoke-return-something-else-handler))
  (restart-case
      (let ((input-xml ""))
    (error 'bad-xml :text input-xml))
    (return-nil () nil)
    (use-value (value) value)))

Comments

Popular posts from this blog

quick note on scala.js, react hooks, monix, auth

zio environment and modules pattern: zio, scala.js, react, query management

user experience, scala.js, cats-effect, IO