如何用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
相关产品推荐
相关产品推荐

