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/or
、s/alt
),并且您 "flowing" 有效替代后续谓词 (s/and
),IMO 在后续谓词中拥有该知识通常更有用。我有兴趣看到此类规范的更实际用例,因为可能有更好的规范方法。
我最近一直在尝试 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/or
、s/alt
),并且您 "flowing" 有效替代后续谓词 (s/and
),IMO 在后续谓词中拥有该知识通常更有用。我有兴趣看到此类规范的更实际用例,因为可能有更好的规范方法。