Last Updated: February 25, 2016
· quoll

recur from catch

Clojure's recur function can only occur in a tail position. However, this does not work directly when using a loop/recur to repeat a possibly failing operation.

In this case, wrap the operation in a thunk, and return the value in a tuple of [result exception], where either the result or the exception is set (that's XOR). Then a simple 'if' can be used to determine if a result is returned or another attempt should be made.

A function that takes a thunk and a number of tries looks like this:

(defn try-loop [thunk tries]
  (loop [n tries ex nil]
    (if (zero? n)
      (and ex (throw ex))
      (let [[result exception] (try
                                  [(thunk) nil]
                                  (catch Throwable t
                                    [nil t]))]
        (if-not exception
          (recur (dec n) (or ex exception)))))))

Comments on this:
- (if (zero? n) - tests if the retries count has finished.
- (and ex (throw ex)) - only throw ex if not nil.
- (recur (dec n) (or ex exception)) - the "(or ex exception)" is used to keep the first exception, instead of the last.