How to save state updates in try / catch blocks in Clojure

So, I have something where I want, in the try block, add various data to some data object, and then in case of an exception, save this record with an error and all data fields received before the exception. In Java, this will be easy. Even if you go with some kind of immutable record type, you can do:

MyRecord record = new MyRecord();
try {
  record = record.withA(dangerouslyGetA());
  record = record.withB(dangerouslyGetB());
  record = record.withC(dangerouslyGetC());
} catch (Exception ex) {
  record = record.withError(ex);
}
save(record);

So, if he bombes in step C, he will save the record with A, B and error.

I cannot find an easy way to do this in Clojure. If you put tryaround a let, then you must assign an “update” record to the new variables each, and therefore they do not fall within the scope of the expression catch. And even if they were, you would not know which one to use.

I think I could put try / catch / let around each expression, but this is a lot more code than the Java version and requires duplication of the statement saveeverywhere. My understanding was that Clojure was great for his patience and easy avoidance of duplication, so something makes me think this is the wrong way.

Of course, this is a fairly common need and has a simple idiomatic solution, right?

+4
source share
1 answer

I think the packaging of each individual statement is actually the most idiomatic solution to this. And if you don't want to write too much, you can create a macro to add exception handling to your single steps.

(defmacro with-error->
  [error-fn value & forms]
  (if-not (seq forms)
    value
    `(let [ef# ~error-fn
           v# ~value]
       (try 
         (with-error-> ef# (-> v# ~(first forms)) ~@(rest forms))
         (catch Exception ex# (ef# v# ex#))))))

->, error-fn ( ), catch:

(with-error-> #(assoc % :error %2) {}
  (assoc :x 0)
  (assoc :y 1)
  (assoc :z (throw (Exception. "oops.")))
  (assoc :a :i-should-not-be-reached))
;; => {:error #<Exception java.lang.Exception: oops.>, :y 1, :x 0}

, , . a atom, , , -fu.

+6

All Articles