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 只是 :ident 分支下 or 的规范,那么规范将其视为有效,但在涉及两个 or 规范时就会崩溃。

我应该如何构造这样的规范呢?

1 答案

+2 投票

被选中
 
最佳答案

::outer 使用 s/keys 和 s/and 组合两个规范。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 给我了我想要的 exactly 的形状
...