所以我有一些我想要的东西,在try块中,向各个数据对象添加各种数据,然后在抛出异常的情况下,保存带有错误的记录,并在异常之前检索所有数据字段。在Java中,这很容易。即使你使用某种不可变类型的记录,你也可以这样做:
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);
所以,如果它在步骤C发生轰炸,那么它将保存A,B和错误的记录。
我无法在Clojure中找出任何直接的方法。如果在try
附近放置let
,则必须将记录的“更新”分配给每个新变量,因此它们不在catch
表达式的范围内。即使它们是,你也不会知道使用哪一个。
我想我可以对每个表达式进行try / catch / let,但这比Java版本的代码要多得多,并且需要在任何地方复制save
语句。我的理解是,Clojure的简洁性和轻松避免重复是很好的,所以有些东西让我觉得这是错误的方法。
当然这是一个相当普遍的需求,并且有一个简单的惯用解决方案,对吗?
答案 0 :(得分:6)
我认为包装每一个语句实际上是最常用的解决方案。如果你不想写太多,你可以构建一个宏来为你的单个步骤添加异常处理。
(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}
当然,您可以随时使用可变状态,例如一个atom
,但我不认为如果你能用一点宏观的话来达到同样的效果。