请分享您的想法,参加2024 Clojure 状态调查!

欢迎!请参阅关于页面,了解更多关于这个网站如何工作的信息。

0
规范

我正在尝试为一个现有数据编写规范。当我尝试将一个包含或的规范放到另一个中时,它就停止工作了。我们大量使用 confrom,所以得到 or 标签很重要。
`

(s/def :a/b int?)
(s/def ::inner (s/or
                 :ident (s/tuple #{:a/b} :a/b)
                 :temp string?))
(s/def ::one int?)
(s/def ::two int?)
(s/def ::outer (s/and
            (s/keys :req [::inner])
                 (s/or
                  :one (s/keys :req [::one])
                  :two (s/keys :req [::two]))))
(s/valid? ::outer {::inner [:a/b 1]
               ::one 1})

`

在上面的例子中,如果 ::inner 只是在 :ident 分支下的规范,那么规范视为有效,但一旦涉及两个 or 规范,它就会中断。

我应该以另一种方式构建此类规范吗?

1 答案

+2

被选中
 
最佳答案

::outer 使用 连接两个 s/keys 规范。s/and 将验证过的规范流动起来,所以第二个规范将看到验证过的值,而不是原始值。同样重要的是,s/keys 总是验证规范中注册的所有键。这些综合起来导致了 ::outer 的失败。

也就是说,当 ::outer 被验证时,它将输入传递给第一个规范,并返回验证过的值 {::inner [:ident [:a/b 1]]},该值作为输入传递给第二个规范。第二个规范看到一个注册的键 ::inner,并尝试将 [:ident [:a/b 1]] 与 ::inner 匹配,这将失败。

我认为这里可能有几个可行的方案,但由于不知道全部情况,很难说哪个是最好的。一个是以 s/merge 代替 s/and,这样做不会把符合的条件值传递给每个规范。所以,在您的例子中,只需将 s/and 替换为 s/merge,就可以使事情符合预期。这里的限制是,你只能得到合并中最后一个规范的符合条件值。不清楚这是否是您想要的。

针对 ::one/::two 情况的另一个选项是,利用 s/keys :req 中内置的 or 支持,将所有这些合并成一个规范

(s/keys :req [::inner (or ::one ::two)])

by
谢谢。  s/merge 给我的形状正好是我想要的
...