Last Updated: February 25, 2016
·
2.09K
· 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
 result
 (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.