Skip to content

Instantly share code, notes, and snippets.

@divs1210
Forked from aphyr/with-retry.clj
Last active October 17, 2022 16:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save divs1210/d29f147a0b7260b60ca084c82ee2ae24 to your computer and use it in GitHub Desktop.
Save divs1210/d29f147a0b7260b60ca084c82ee2ae24 to your computer and use it in GitHub Desktop.
Recur from within catch block
(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 (list? 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)))))
(->> (with-retry [x 0 y 0]
[(/ 1 x) y]
(catch Exception e
(retry (inc x) (inc y))))
quote macroexpand pprint)
(loop*
[x 0 y 0]
(clojure.core/let
[retval869
(try
[(/ 1 x) y]
(catch Exception e (user.Retry. [(inc x) (inc y)])))]
(if
(clojure.core/instance? user.Retry retval869)
(recur
(clojure.core/nth (.bindings retval869) 0)
(clojure.core/nth (.bindings retval869) 1))
retval869)))
(with-retry [x 0 y 0]
[(/ 1 x) y]
(catch Exception e
(retry (inc x) (inc y))))
; => [1 1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment