;;;; What a handler for a parent reads as the message, and that it is still ;;;; there after the unwind. The clause below runs after the signalling frames ;;;; are gone, and [scribble] reuses their stack before the message is ;;;; printed, so a message left pointing into them prints as garbage. (defstruct IoError :parent Error) (defstruct Empty :parent Error []) (defstruct MyErr :parent Error [code i32 why str]) (defonce zero i64) ;; Deep enough, and wide enough, to overwrite what the signal left below. (defn scribble [n i32] i64 (let [junk (array-fill [64] (i64 n))] (if (= n 0) (at junk 3) (+ (at junk 5) (scribble (- n 1)))))) (defn caught [thunk (Fn [] i32)] i32 (handler-case (thunk) [(Error [e] (scribble 40) (println (.name e)) (println (.message e)) -1)])) (defn main [] i32 ;; A category the program filled in is its own name and message. (caught (fn [] (do (error (IoError {.name "io" .message "disk gone"})) 0))) ;; An empty field vector is the same category. (caught (fn [] (do (error (Empty {.name "empty" .message "nothing"})) 0))) ;; A program's own condition is printed with its values. (caught (fn [] (do (error (MyErr {.code 3 .why "bad"})) 0))) ;; A runtime condition carries the sentence with its values. (caught (fn [] (i32 (/ 7 zero)))) 0)