如何在Clojure中将已有断言向量应用到:post后置条件中
Clojure 函数:post条件动态传入预制验证向量的解决方案
问题根因
defn是编译期执行的宏,它会在编译阶段直接处理函数元数据中的:pre/:post条件,这些条件必须是编译期可获取的字面量集合,其中的%是defn宏内部做的特殊语法替换,会被替换为函数的返回值,运行期定义的变量在编译期无法被defn宏识别,因此会抛出语法错误。- 直接在
:post值的位置调用自定义宏会报错,是因为defn宏处理元数据的优先级高于内部宏的展开,它会把宏调用当成普通列表处理,自然无法获取宏的值。
解决方案
方案1:自定义包裹宏,编译期注入公共post条件
如果你的验证规则是固定的、编译期就能确定的,可以通过自定义包裹defn的宏,将预制的验证条件在编译期插入到:post元数据中:
;; 预制公共post验证条件,用quote包裹为字面量表达式列表 (def common-post-conds '[(pos? %) (int? %) (= 10 %)]) ;; 自定义defn包裹宏,自动注入公共post条件 (defmacro defn-with-common-post [fn-name params & body] `(defn ~fn-name ~params {:post ~(vec common-post-conds)} ~@body)) ;; 使用示例 (defn-with-common-post foo [x] x)
测试验证:
- 调用
(foo 10)正常返回10 - 调用
(foo 9)会直接抛出post条件校验失败的异常,和原生硬编码:post的效果完全一致。
方案2:手动实现运行期动态校验,不依赖原生:post
如果你需要运行期动态调整验证规则,不用原生:post语法糖,在函数内部手动校验返回值即可,灵活度更高:
;; 预制验证谓词向量,可在运行期动态修改 (def my-post-validator [pos? int? #(= 10 %)]) (defn foo [x] (let [res x] ;; 遍历所有验证器校验返回值 (doseq [pred my-post-validator] (when-not (pred res) (throw (ex-info "Post condition failed" {:result res :failed-predicate pred})))) res))
这种方案的优势是my-post-validator可以在运行期动态增删改验证规则,不需要重新编译函数,适配需要动态调整校验逻辑的场景。
内容的提问来源于stack exchange,提问作者I was in the neighborhood
相关产品推荐
相关产品推荐

