Conditions and Handlers

36.1 Signaling and Handling Errors

(error "Something went wrong: ~a" 42)        ; signals a SIMPLE-ERROR
(warn "Low disk space")                      ; signals a WARNING (prints, continues)
(signal 'my-condition)                       ; signals without requiring handling
(assert (> 1 0) () "Math is broken")         ; assertion (offers a continue restart)
(check-type x integer)                       ; type assertion with a store-value restart

;; handler-case: like try/catch (unwinds the stack to the handler)
(handler-case (/ 10 0)
  (division-by-zero (c)
    (format nil "Caught: ~a" c))
  (error (c)
    (format nil "Other error: ~a" c)))

(handler-case (parse-integer "abc")
  (parse-error () :invalid)
  (:no-error (value position)                ; runs when no error occurred
    (declare (ignore position))
    (list :ok value)))

;; ignore-errors: return NIL and the condition on error
(ignore-errors (/ 1 0))                      ; NIL, #<DIVISION-BY-ZERO>

36.2 Defining Condition Types

Standard condition types condition → serious-condition → error

simple-error, type-error, program-error, control-error

arithmetic-error → division-by-zero, floating-point-overflow

cell-error → unbound-variable, undefined-function, unbound-slot

file-error, stream-error → end-of-file, parse-error, reader-error

warning → style-warning, simple-warning Out of memory, stack exhaustion

(define-condition bank-error (error) ())

(define-condition insufficient-funds (bank-error)
  ((balance :initarg :balance :reader balance)
   (requested :initarg :requested :reader requested))
  (:report (lambda (c stream)
             (format stream "Insufficient funds: balance ~a, requested ~a"
                     (balance c) (requested c)))))

(defun withdraw* (balance amount)
  (when (> amount balance)
    (error 'insufficient-funds :balance balance :requested amount))
  (- balance amount))

(handler-case (withdraw* 50 100)
  (insufficient-funds (c)
    (format t "~a (short by ~a)~%" c (- (requested c) (balance c)))))
;; Insufficient funds: balance 50, requested 100 (short by 50)

36.3 handler-bind: Handling Without Unwinding

;; handler-bind runs the handler AT THE POINT OF THE SIGNAL; the stack is still intact.
;; The handler may: decline (return normally → the search continues), unwind (non-local exit),
;; or invoke a restart (Module 37).
(handler-bind ((warning (lambda (c)
                          (format t "~&Logged warning: ~a~%" c)
                          (muffle-warning c))))   ; invoke the MUFFLE-WARNING restart
  (warn "deprecated function used")
  :continued)                                   ; :CONTINUED

(handler-case                                   ; outer: unwinds and recovers
    (handler-bind ((error (lambda (c)           ; inner: runs first, logs, then declines
                            (format *error-output* "~&Error logged: ~a~%" c))))
      (error "boom"))
  (error () :recovered))                        ; → :RECOVERED (after the log line)