;;;; handler-bind and signal — spec-conditions.md §1 and §2. ;;;; ;;;; The accumulation case, which is what makes these two worth having on their ;;;; own: signal returns Unit, a handler that returns normally leaves the ;;;; signalling function to carry on, and with nothing matching signal is a ;;;; no-op. No control flow is altered, so none of the transfer machinery ;;;; restart-case needs exists yet. (defstruct AssetMissing [id i32]) (defstruct Corrupt [id i32]) (defvar seen i64) (defvar other i64) ;;; Signals twice and keeps going both times — that is the whole of §1. (defn load-all [] (signal (AssetMissing {:id 1})) (signal (AssetMissing {:id 2})) (signal (Corrupt {:id 3}))) (defn main [] i32 ;; No handler: a no-op, not an abort and not a message (§2). (load-all) (print seen) (println "") ; 0 (handler-bind [(AssetMissing [c] (set seen (+ seen (i64 (.id c)))))] (load-all)) (print seen) (println "") ; 1 + 2 = 3 ;; Two clauses, and only the matching one runs for each condition. (handler-bind [(AssetMissing [c] (set seen (+ seen 10))) (Corrupt [c] (set other (+ other (i64 (.id c)))))] (load-all)) (print seen) (println "") ; 3 + 20 = 23 (print other) (println "") ; 3 ;; Nesting: the inner frame does not displace the outer one, so both run. (handler-bind [(Corrupt [c] (set other (+ other 100)))] (handler-bind [(Corrupt [c] (set other (+ other 1000)))] (signal (Corrupt {:id 0})))) (print other) (println "") ; 3 + 1000 + 100 = 1103 ;; And the stack is back to what it was: no handler, no effect. (load-all) (print other) (println "") ; 1103 0)