2024 Clojure状态调查中分享您的想法!

欢迎!请参阅关于页面,了解此处的工作方式。

0
Spec

我正在尝试为现有数据写规范。当我尝试将一个包含“or”的规范放入其他规范中时,它就停止工作。我们广泛使用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只是位于or:ident分支下的规范,则规范将其视为有效,但是在有两个or规范的情况下则破裂。

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

1 答案

+2

已选择
 
最佳答案

::outer通过s/and组合两个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)])

谢谢。  s/merge 给了我我正寻找的形状
...