I am trying to execute a func several times before giving up upon exceptions. But it is not valid in Clojure to recur from catch block. How can this be achieved ?
(loop [tries 10] (try (might-throw-exception) (catch Exception e (when (pos? tries) (recur (dec tries)))))) java.lang.UnsupportedOperationException: Cannot recur from catch/finally
The best I could find is the following clumsy solution (wrapping in func and calling it)
(defn do-it [] (try (might-throw-exception) (catch Exception e nil))) (loop [times 10] (when (and (nil? (do-it)) (pos? times)) (recur (dec times))))
Macros are calling...
How about this:
(defn try-times* "Executes thunk. If an exception is thrown, will retry. At most n retries are done. If still some exception is thrown it is bubbled upwards in the call chain." [n thunk] (loop [n n] (if-let [result (try [(thunk)] (catch Exception e (when (zero? n) (throw e))))] (result 0) (recur (dec n))))) (defmacro try-times "Executes body. If an exception is thrown, will retry. At most n retries are done. If still some exception is thrown it is bubbled upwards in the call chain." [n & body] `(try-times* ~n (fn [] ~@body)))
kotarak's idea is the way to go, but this question tickled my fancy so I'd like to provide a riff on the same theme that I prefer because it doesn't use loop/recur:
(defn try-times* [thunk times] (let [res (first (drop-while #{::fail} (repeatedly times #(try (thunk) (catch Throwable _ ::fail)))))] (when-not (= ::fail res) res)))
And leave the try-times macro as it is.
If you want to allow the thunk to return nil, you can drop the let/when pair, and let ::fail represent "the function failed n times", while nil means "the function returned nil". This behavior would be more flexible but less convenient (the caller has to check for ::fail to see if it worked rather than just nil), so perhaps it would be best implemented as an optional second parameter:
(defn try-times* [thunk n & fail-value] (first (drop-while #{fail-value} ...)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With