Clojure: How to to recur upon exception?
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} ...)))