在 Clojure 中如何使用带有 :post 条件的现有谓词向量?

How do you use an existing vector of predicates with :post conditions in Clojure?

鉴于 :post 采用稍后评估的形式(例如 {:post [(= 10 %)]})。如何动态地将 'pre-made' 函数向量传递给 :post

例如:

(def my-post-validator
  [prediate1 predicate2 predicate3])
   

(defn foo [x] 
  {:post my-post-validator}
  x)

这会引发语法错误

Don't know how to create ISeq from: clojure.lang.Symbol

以我的模糊理解,是因为defn是一个宏,而允许:post中的%语法的是它在内部被引用..?

我想也许我然后使用宏来传递 'literal' 我想要评估的内容

(defmacro my-post-cond [spec]
  '[(assert spec %) (predicate2 %) (predicate n)])

示例:

(defn foo [x]
  {:post (my-post-cond :what/ever)}
  x)

然而,这种尝试给出了错误:

Can't take value of a macro

有没有办法将事物的向量传递给 :post 而不必内联定义它?

您不能传递预定义谓词的向量,但您可以将多个谓词组合在一个名称下并在 :post:

中使用该名称
(defn my-post-cond [spec val]
  (and
    ;; Not sure if this is exactly what you want,
    ;; given that `val` becomes an assert message.
    (assert spec val)
    (predicate2 val)
    ;; You used `n` - I assume it was supposed to be `%`.
    (predicate val)))

(defn foo [x]
  {:post [(my-post-cond :what/ever %)]}
  x)

我最初是 pre-post-conditions 的粉丝,但这些年来我已经改变了。

对于简单的事情,我更喜欢使用 Plumatic Schema 不仅可以测试输入和输出,还可以记录它们。

对于更复杂的测试和验证,我只是输入了一个明确的 assert 或类似的。我还在 the Tupelo library 中写了一个辅助函数来减少调试或验证 return 值时的重复等:

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(defn oddly
  "Transforms its input. Throws if result is not odd"
  [x]
  (let [answer (-> x (* 3) (+ 2))]
    (with-result answer
      (newline)
      (println :given x)
      (assert (odd? answer))
      (println :returning answer))))

(dotest
  (is= 5 (oddly 1))
  (throws? (oddly 2)))

结果

------------------------------------
   Clojure 1.10.3    Java 11.0.11
------------------------------------

Testing tst.demo.core

:given 1
:returning 5

:given 2

Ran 2 tests containing 2 assertions.
0 failures, 0 errors.

Passed all tests

因此,使用 printlnassert,returned 值很容易看到。如果失败 assert,将正常抛出异常。