我有一些我想线程化的操作,每个操作都可能失败。我宁愿将错误作为值而不是使用try-catch来破坏执行流程。
我可以执行朴素的版本,并使函数将nil用作失败:
(if-let (op1 ...)
(if-let (op2 ...)
...
err1)
err2)
但是它是嵌套的,很难阅读。
我可以使用some->
,这似乎是最接近的解决方案,但它没有说明失败的原因:
(if-let [res (some-> arg
op1
op2)]
res
somethin-failed) ;; what failed though?
我也查看了->
和cond->
,但它们似乎没有帮助。
我知道在线上有宏可以执行此类操作,但是如果有解决方案,我宁愿不添加宏。希望有某种形式:
(some-with-err-> arg
op1 err1
op2 err2
...)
我可能忽略了一些简单的东西,但似乎找不到内置的东西来解决此问题。
我可以编写一个宏来执行此操作,但现在宁愿避免使用它。
答案 0 :(得分:3)
没有内置的功能,但是似乎可以找到想要的单子错误处理库(例如Failjure)。
您可以从some-with-err->
宏定义中导出版本some->
。唯一实际的区别是,绑定到map
的{{1}}函数现在可以对表单/错误值进行分区,将steps
的调用包装在step
中,并在失败时返回一个命名空间映射: / p>
try
它可以像(defmacro some-with-err->
[expr & forms]
{:pre [(even? (count forms))]}
(let [g (gensym)
steps (map (fn [[step error]]
`(if (or (nil? ~g) (::error ~g))
~g
(try (-> ~g ~step)
(catch Exception _# {::error ~error}))))
(partition 2 forms))]
`(let [~g ~expr
~@(interleave (repeat g) (butlast steps))]
~(if (empty? steps)
g
(last steps)))))
一样使用,但是每种形式都必须带有错误返回值:
some->