在累积所有违规行为的同时在 Clojure 中进行多项检查的最佳方法

Best way to do multiple checks in Clojure while accumulating all violations

我有几个 Clojure 谓词,它们根据不同的业务规则验证应用程序输入。考虑到这一点,我想知道遍历这一系列谓词的最佳策略是什么:

我考虑过以下方法,但我对此表示怀疑。

(defn validate [arg]
  (cond
    (logic/check-one? arg) (add-violation-one)
    (logic/check-two? arg) (add-violation-two)))

一种方法是使用 cond->。它运行所有检查并且它 向下传递分支的结果。例如

(let [arg 42
      add-error #(update %1 :errors conj %2)] 
  (cond-> {:errors []}
    (even? arg) (add-error "must not be even")
    (odd? arg)  (add-error "must not be odd")
    (zero? arg) (add-error "must not be zero")
    (pos? arg)  (add-error "must not be positive")))
; → {:errors ["must not be even" "must not be positive"]}

这种方法最有意义,如果你真的想写出那些 手工的东西。不直接依赖可能更灵活 来自核心的东西并编写你自己的功能。例如。你可以减少 在谓词和错误处理程序的元组上。

(defn validate 
  [checks errors arg]
  (reduce 
    (fn validation-step [errors [pred handle-error]]
      (if-let [result (pred arg)]
        (update errors :errors conj (handle-error result))
        errors))
    errors
    checks))

(prn
  (validate
    [[even? (constantly "must not be even")]
     [odd?  (constantly "must not be odd")]
     [zero? (constantly "must not be zero")]
     [pos?  (constantly "must not be positive")]]
    {:errors []}
    42))
; → {:errors ["must not be even" "must not be positive"]}