|
116 | 116 | :sequence (recur (::process-node node) |
117 | 117 | (conj inputs (::input node)))))) |
118 | 118 |
|
| 119 | +(defn catching |
| 120 | + "Like `clojure.core/comp`, except only affects throw completions of |
| 121 | + the wrapped `g`. `f` must be a function of a single Exception, and |
| 122 | + will be passed the exception thrown from `g` if `g` completes |
| 123 | + exceptionally." |
| 124 | + [f g] |
| 125 | + (fn [& args] |
| 126 | + (try (apply g args) |
| 127 | + (catch Throwable t (f t))))) |
| 128 | + |
| 129 | +(defn update-leaf |
| 130 | + [leaf f compositor] |
| 131 | + ;; f new function, arg is old function |
| 132 | + (update leaf :nodely.data/fn #(compositor f %))) |
| 133 | + |
| 134 | +(declare env-update-helper) |
| 135 | + |
| 136 | +(defn update-branch |
| 137 | + [{::keys [condition truthy falsey]} |
| 138 | + f |
| 139 | + {:keys [apply-to-condition?] |
| 140 | + :or {apply-to-condition? false} :as opts} |
| 141 | + compositor] |
| 142 | + #::{:type :branch |
| 143 | + :condition (if apply-to-condition? |
| 144 | + (env-update-helper condition f opts compositor) |
| 145 | + condition) |
| 146 | + :falsey (env-update-helper falsey f opts compositor) |
| 147 | + :truthy (env-update-helper truthy f opts compositor)}) |
| 148 | + |
| 149 | +(defn update-sequence |
| 150 | + [sequence f compositor] |
| 151 | + (update sequence ::process-node env-update-helper f {} compositor)) |
| 152 | + |
| 153 | +(defn env-update-helper |
| 154 | + [node f opts compositor] |
| 155 | + (case (::type node) |
| 156 | + :value (update node ::value f) |
| 157 | + :leaf (update-leaf node f compositor) |
| 158 | + :branch (update-branch node f opts compositor) |
| 159 | + :sequence (update-sequence node f compositor))) |
| 160 | + |
| 161 | +(defn update-node |
| 162 | + ([node f opts] |
| 163 | + (env-update-helper node f opts comp)) |
| 164 | + ([node f] |
| 165 | + (update-node node f {}))) |
| 166 | + |
| 167 | +(defn catch-node |
| 168 | + ([node f opts] |
| 169 | + (env-update-helper node f opts catching)) |
| 170 | + ([node f] |
| 171 | + (catch-node node f {}))) |
| 172 | + |
119 | 173 | ;; |
120 | 174 | ;; Env Utils |
121 | 175 | ;; |
122 | 176 |
|
| 177 | +(defn with-error-handler |
| 178 | + [env handler] |
| 179 | + (update-vals env #(catch-node % handler {:apply-to-condition? true}))) |
| 180 | + |
| 181 | +(defn- tuple-to-handler |
| 182 | + [m] |
| 183 | + (fn [error] |
| 184 | + (if-let [f (some (fn [[ex-class handler]] (when (instance? ex-class error) handler)) m)] |
| 185 | + (f error) |
| 186 | + (throw error)))) |
| 187 | + |
| 188 | +(defn- with-try-expr |
| 189 | + [clauses] |
| 190 | + (let [clauses (into [] (for [[c t s expr] clauses] |
| 191 | + (do (assert (= c 'catch)) |
| 192 | + (if-let [t (resolve t)] |
| 193 | + [t (eval `(fn [~s] ~expr))] |
| 194 | + (throw (ex-info (str "Could not resolve exception class: " t) {:type t}))))))] |
| 195 | + clauses)) |
| 196 | + |
| 197 | +(defmacro with-try |
| 198 | + "Macro |
| 199 | +
|
| 200 | + `with-try` will apply an error handling semantic to every leaf, |
| 201 | + branch, and sequence node in a provided environment. If any such |
| 202 | + nodes throw an exception, the catch expressions described in the |
| 203 | + body of `with-try` will be evaluated. The resulting value of the |
| 204 | + matching catch clause will become the value of the node which threw |
| 205 | + the exception. |
| 206 | +
|
| 207 | + `with-try` has syntax equivalent to Clojure's `try` special form for |
| 208 | + `catch` clauses but does not currently support use of a `finally` |
| 209 | + clause. |
| 210 | +
|
| 211 | + `with-try` creates a policy across an entire environment |
| 212 | + indiscriminately, when it is possible, clients are advised to catch |
| 213 | + exceptional cases in the implementations of leaf/branch/sequence |
| 214 | + nodes explicitly. |
| 215 | +
|
| 216 | + example: |
| 217 | +
|
| 218 | + (with-try {:a (>leaf (/ 5 ?b)) |
| 219 | + :b (>value 0)} |
| 220 | + (catch ArithmeticException _ Double/NaN) |
| 221 | + (catch NullPointerException _ 0)) |
| 222 | +
|
| 223 | + will result in `:a` evaluating to 0" |
| 224 | + [env & body] |
| 225 | + `(with-error-handler |
| 226 | + ~env |
| 227 | + (tuple-to-handler ~(with-try-expr body)))) |
| 228 | + |
123 | 229 | (s/defn get-value :- s/Any |
124 | 230 | [env :- Env |
125 | 231 | k :- s/Keyword] |
|
0 commit comments