;;;; restart-case and invoke-restart — spec-conditions.md §3 to §6. ;;;; ;;;; The transfer. A handler runs where the signal was, decides, and control ;;;; resumes at a restart-case further out: every function in between returns ;;;; early with the target in the channel, running its defers on the way (§5). ;;;; Restarts take no parameters in this version. (defstruct AssetMissing [id i32]) (defvar log i64) ;;; The signalling end. Two frames below the restart-case, so the transfer has ;;; something to cross. (defn load [n i32] i32 (signal (AssetMissing {:id n})) 100) ;;; §5: this defer runs whether the call below returns or transfers, and it ;;; runs before the clause body starts. (defn middle [n i32] i32 (defer (set log (+ log 1))) (+ (load n) 1)) ;;; The spec's load-texture shape (§1): a restart-case in value position, whose ;;; fall-through has to produce the type too. (defn fetch [n i32] i32 (restart-case (middle n) (use-placeholder [] -1) (retry [] 7))) ;;; §4: an inner restart-case shadows an outer one offering the same name, and ;;; the outer one is reached again once the inner has been left. (defn nested [n i32] i32 (restart-case (+ (restart-case (middle n) (use-placeholder [] 10)) 1000) (use-placeholder [] 20))) ;;; §2's diverging variant. Same lookup, but a handler that returns normally ;;; has not answered it: only a transfer gets past, so the fall-through the ;;; signalling version needs does not exist here. (defn strict [n i32] i32 (restart-case (do (error (AssetMissing {:id n})) ;; unreachable — error is Never, so nothing after it runs 0) (use-placeholder [] -2))) (defn main [] i32 ;; Nothing handles it, so signal is a no-op and the body's own value stands. (print (fetch 1)) (println "") ; 101 (print log) (println "") ; 1 ;; A handler that transfers: the clause's value is the restart-case's. (handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))] (print (fetch 2)) (println "")) ; -1 (print log) (println "") ; 2 — the defer ran (handler-bind [(AssetMissing [c] (invoke-restart 'retry))] (print (fetch 3)) (println "")) ; 7 ;; §4: the innermost frame offering the name wins, and the clause yields to ;; *its* own continuation — so the +1000 written around the inner ;; restart-case still runs, and the outer clause never does. (handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))] (print (nested 4)) (println "")) ; 1010 ;; A handler that returns normally transfers nothing: §1's accumulation case ;; still works, and the fall-through stands. (handler-bind [(AssetMissing [c] (set log (+ log 100)))] (print (fetch 5)) (println "")) ; 101 ;; The handler runs at the signal, which is inside the call the defer ;; belongs to, so its +100 lands before that defer's +1. (print log) (println "") ; 4 + 100 + 1 = 105 ;; error, answered by a transfer. Unanswered it stops the program, which is ;; the trap case in the acceptance table rather than a line here. (handler-bind [(AssetMissing [c] (invoke-restart 'use-placeholder))] (print (strict 6)) (println "")) ; -2 0)