clojure spec fdef无效 - 通过递归定义

时间:2017-06-25 22:14:18

标签: clojure specifications

这是我为语法打嗝编写的clojure规范。

(:require
   [clojure.spec.alpha :as s]
   [clojure.spec.test.alpha :as st]
   ))


(s/def ::tag (s/and
              keyword?
              #(re-matches #":[a-z]+([0-9]+)?"
                           (str  %))))

(s/def ::org-content (s/cat
                      :tag ::tag
                      :content (s/+ (s/or
                                     :str string?
                                     :content ::org-content
                                     ))))

我在这里写了一个简单的函数规范 -

(s/fdef org-headers-h3
        :args (s/cat :contact ::org-content)
        :ret keyword?)

(defn org-headers-h3 [doc]
  (first doc))

(st/instrument `org-headers-h3)

(org-headers-h3 [:div [:h1 "d"]])

导致以下错误 -

*Call to #'modcss2.parser/org-headers-h3 did not conform to spec:
   In: [0] val: [:div [:h1 "d"]] fails spec: :modcss2.parser/tag at:
   [:args :contact :tag] predicate: keyword?  :clojure.spec.alpha/spec
   #object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x6613f384
   "clojure.spec.alpha$regex_spec_impl$reify__1200@6613f384"]
   :clojure.spec.alpha/value ([:div [:h1 "d"]])
   :clojure.spec.alpha/args ([:div [:h1 "d"]])
   :clojure.spec.alpha/failure :instrument*

我似乎对我说,我因错误的参数类型而得到错误。  但我对以下声明的看法是正确的。

(s/valid? ::org-content [:div [:h1 "d"]]) => true

1 个答案:

答案 0 :(得分:2)

另一个s/cat并不代表他们的嵌套,他们只是连接起来。这适用于所有正则表达式规范。

(s/conform (s/cat :foo (s/cat :bar int?)) [1]) ;=> {:foo {:bar 1}}

(s/conform (s/cat :foo (s/cat :bar int?)) [[1]]) ;=> ::s/invalid

您的:args规范扩展为:(s/cat :contact (s/cat :tag ::tag ,,,))

要嵌套它们,您可以使用s/spec

(s/conform (s/cat :foo (s/spec (s/cat :bar int?))) [[1]]) ;=> {:foo {:bar 1}}

所有正则表达式规范都是这样连接的。在这种情况下,使用s/and来检查它是否为向量并使其成为非正则表达式规范以使其正常嵌套是有意义的。

(s/conform (s/cat :foo (s/and vector? (s/cat :bar int?))) [[1]]) ;=> {:foo {:bar 1}}

所以,要解决你的问题:

(s/def ::org-content
  (s/and vector?
         (s/cat
          :tag ::tag
          :content (s/+ (s/or
                         :str string?
                         :content ::org-content)))))