Skip to content

Instantly share code, notes, and snippets.

@aphyr
Last active August 15, 2016 23:02
Show Gist options
  • Save aphyr/9c5d6f2d95b1fa2f014a9650db615f50 to your computer and use it in GitHub Desktop.
Save aphyr/9c5d6f2d95b1fa2f014a9650db615f50 to your computer and use it in GitHub Desktop.
with-retry
(defrecord Retry [bindings])
(defmacro with-retry
"It's really inconvenient not being able to recur from within (catch)
expressions. This macro wraps its body in a (loop [bindings] (try ...)).
Provides a (retry & new bindings) form which is usable within (catch) blocks:
when this form is returned by the body, the body will be retried with the new
bindings."
[initial-bindings & body]
(assert (vector? initial-bindings))
(assert (even? (count initial-bindings)))
(let [bindings-count (/ (count initial-bindings) 2)
body (walk/prewalk (fn [form]
(if (and (seq? form)
(= 'retry (first form)))
(do (assert (= bindings-count
(count (rest form))))
`(Retry. [~@(rest form)]))
form))
body)
retval (gensym 'retval)]
`(loop [~@initial-bindings]
(let [~retval (try ~@body)]
(if (instance? Retry ~retval)
(recur ~@(->> (range bindings-count)
(map (fn [i] `(nth (.bindings ~retval) ~i)))))
~retval)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment