1

考虑以下文本或链路层端口号规范:

(require '[clojure.spec.alpha :as spec])

(spec/def ::text (spec/and string? not-empty))
(spec/valid? ::text "a")                ; => true
(spec/valid? ::text "")                 ; => false
(spec/def ::port (spec/and pos-int? (partial > 65535)))
(spec/valid? ::port 4)                  ; => true
(spec/valid? ::port 0)                  ; => false
(spec/def ::text-or-port (spec/or ::text ::port))
(spec/valid? ::text-or-port 5)          ; => true
(spec/valid? ::text-or-port "hi")       ; => false

出于某种原因,它只接受端口号而不接受文本,为什么会这样?

4

1 回答 1

2

理解这个问题的关键可以在文档和使用中找到spec/conform

(spec/conform ::text-or-port 5)
; => [:user/text 5]

问题是它clojure.spec.alpha/or有一个与clojure.core/or给定两个参数返回第一个真实参数的 API 不同的 API:

(#(or (string? %) (integer? %)) 5)      ; => true
(#(or (string? %) (integer? %)) "")     ; => true
(#(or (string? %) (integer? %)) :a)     ; => false

相反,它需要成对的标签和规范/谓词。而且由于即使命名空间的关键字也被接受为标签::text-or-port,所以 OP 中给出的规范只匹配通过要求::port并给它标签的规范::text。以下是我们要匹配的正确规范:

(spec/def ::text-or-port (spec/or :text ::text
                                  :port ::port))
(spec/valid? ::text-or-port "hi")       ; => true
(spec/valid? ::text-or-port 10)         ; => true
于 2017-05-23T12:15:37.283 回答