Search code examples
clojure

Clojure: How to to recur upon exception?


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))))

Solution

  • 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)))