You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Clojure Spec确保add-item函数不混加书籍与借阅者?

搞定Clojure Spec对add-item的类型约束问题

你当前的fdef之所以不生效,问题出在参数Spec的写法上——既没必要嵌套s/fspec,用s/and组合两个s/cat更是逻辑错误,这会让Spec要求参数同时满足两种完全矛盾的结构,自然起不到约束作用。

先说说你的写法哪里错了

这段代码的问题很明显:

:args (s/fspec
        :args (s/or
                :is-brs (s/and (s/cat :x ::br/borrower) (s/cat :xs ::brs))
                :is-bks (s/and (s/cat :x ::bk/book) (s/cat :xs ::bks))))
  • 多套了一层s/fspec:s/fdef的:args字段直接接受描述函数参数的Spec,不需要再嵌套fspec
  • s/and用错了:s/cat :x ::br/borrower匹配的是单个参数(只有x),s/cat :xs ::brs也是匹配单个参数(只有xs),而你的add-item是两个参数,用s/and把这两个cat放一起,相当于要求参数同时是单个借阅者和单个借阅者集合,这根本不可能匹配成功

正确的Spec写法

我们需要直接用s/or来定义两种合法的参数组合,每个组合用s/cat来匹配两个参数的类型配对:

(s/fdef add-item
  ;; 定义两种合法的参数组合:借阅者加到借阅者集合,或书籍加到书籍集合
  :args (s/or
          :add-borrower (s/cat :x ::br/borrower :xs ::brs)
          :add-book (s/cat :x ::bk/book :xs ::bks))
  ;; 返回值对应输入的集合类型
  :ret (s/or
         :brs ::brs
         :bks ::bks)
  ;; 可选:验证函数的业务逻辑是否正确(元素存在返回原集合,否则添加后返回)
  :fn (s/or
        :added-borrower (s/and
                          (fn [{{:keys [x xs]} :args} ret]
                            (if (some #{x} xs)
                              (= ret xs)
                              (= ret (cons x xs))))
                          (s/keys :req [:args/add-borrower :ret/brs]))
        :added-book (s/and
                      (fn [{{:keys [x xs]} :args} ret]
                        (if (some #{x} xs)
                          (= ret xs)
                          (= ret (cons x xs))))
                      (s/keys :req [:args/add-book :ret/bks]))))

效果验证

现在如果尝试做跨类型添加,比如把书籍塞进借阅者集合,Spec会立刻报错:

;; 假设book1是符合::bk/book的实例,borrowers是合法的::brs集合
(s/explain #'add-item [book1 borrowers])
;; 输出错误提示:Value does not match any clause in: [:add-borrower :add-book]

而合法的添加操作(比如把借阅者加到借阅者集合)会顺利通过验证。

内容的提问来源于stack exchange,提问作者ericky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:12:45