Clojure 规范 - spec/or 嵌套在 spec/and 中的问题

Clojure Spec - Issue with spec/or nested in spec/and

我最近一直在尝试 Clojure Spec 并且 运行 出现意外错误消息。我发现如果 spec/or 嵌套在 spec/and 中,那么规范函数在 spec/or b运行ch 之后,会传递一个一致的值而不是顶级值。

您可以在此处 "v" 的打印值中看到这一点(人为的示例):

(spec/valid? (spec/and (spec/or :always-true (constantly true))
                       (fn [v]
                         (println "v:" v)
                         (constantly true)))
         nil)
v: [:always-true nil]
=> true

我认为这可能是 spec/and:

的文档字符串中故意的

Takes predicate/spec-forms, e.g.

(s/and even? #(< % 42))

Returns a spec that returns the conformed value. Successive conformed values propagate through rest of predicates.

但这对我来说似乎违反直觉,因为它会妨碍规范谓词的重用,因为它们需要被写入以接受“[ ]”。

如果你有多个 spec/or b运行ches,事情会变得更糟:

(spec/valid? (spec/and (spec/or :always-true (constantly true))
                       (spec/or :also-always-true (constantly true))
                       (fn [v]
                         (println "v:" v)
                       (constantly true)))
         nil)
v: [:also-always-true [:always-true nil]]
=> true

我是不是漏掉了一些基本的东西?

But this seems counterintuitive to me as it would hamper reuse of spec predicates

IMO,这些行为的替代方案不太吸引人:

  • 默认丢弃s/or的符合标签。如果我们愿意,我们可以随时丢弃它,但我们不希望 clojure.spec 为我们做出决定。规范假定我们想知道哪个s/or分支匹配。
  • 不要以 spec/predicate 可组合性为代价在 s/and 中流动符合的值。

幸运的是,我们可以在必要时丢弃 s/or 标签。这里有两个选项:

  • s/or 包裹在 s/noncomforming 中。感谢 glts 在下面的评论提醒我这个(未记录的)函数!

    (s/valid?
      (s/and
        (s/nonconforming (s/or :s string? :v vector?))
        empty?)
      "")
    => true
    
  • s/and s/or 规格 s/conformer 丢弃标签。

    (s/valid?
      (s/and
        (s/and (s/or :s string? :v vector?)
               ;; discard `s/or` tag here
               (s/conformer second))
        empty?)
      [])
    => true
    

    如果你经常需要这个,你可以用一个宏来减少样板文件:

    (defmacro dkdc-or [& key-pred-forms]
      `(s/and (s/or ~@key-pred-forms) (s/conformer second)))
    

Things get even worse if you have multiple spec/or branches

如果您编写的数据规范允许 备选方案(例如 s/ors/alt),并且您 "flowing" 有效替代后续谓词 (s/and),IMO 在后续谓词中拥有该知识通常更有用。我有兴趣看到此类规范的更实际用例,因为可能有更好的规范方法。