clojure.spec/unform 返回不合格的值

Hoa*_*ael 5 clojure clojurescript

大部分情况下,我与 clojure.spec 相处得很好。然而,在处理的过程中,我遇到了一个无法解决的问题unform。这是一个让我们行动起来的 Hiccup 的宽松规范:

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

(s/def ::hiccup
  (s/and
      vector?
      (s/cat
        :name       keyword?
        :attributes (s/? map?)
        :contents   (s/* ::contents))))

(s/def ::contents
  (s/or
    :element-seq (s/* ::hiccup)
    :element     ::hiccup
    :text        string?))
Run Code Online (Sandbox Code Playgroud)

现在,在我们得意忘形之前,让我们看看它是否适用于小型路过的情况。

(def example [:div])

(->> example
     (s/conform ::hiccup))

;;=> {:name :h1}
Run Code Online (Sandbox Code Playgroud)

奇迹般有效。但我们可以撤销我们的一致性吗?

(->> example
     (s/conform ::hiccup)
     (s/unform ::hiccup))

;;=> (:div)
Run Code Online (Sandbox Code Playgroud)

嗯,那应该是一个向量。我错过了什么吗?让我们看看规范对此有什么规定。

(->> example
     (s/conform ::hiccup)
     (s/unform ::hiccup)
     (s/explain ::hiccup))

;; val: (:div) fails spec: :user/hiccup predicate: vector?
;;=> nil
Run Code Online (Sandbox Code Playgroud)

确实,它失败了。所以问题是:我怎样才能让它正常工作?

sbe*_*nsu 0

为了有价值,我制定了一个规范来检查向量并对向量进行非形式化:

(defn vector-spec
  "Create a spec that it is a vector and other conditions and unforms to a vector.

  Ex (vector-spec (s/spec ::binding-form))
     (vector-spec (s/* integer?))"
  [form]
  (let [s (s/spec (s/and vector? form))]
    (reify
      s/Specize
      (specize* [_] s)
      (specize* [_ _] s)

      s/Spec
      (conform* [_ x] (s/conform* s x))
      (unform* [_ x] (vec (s/unform* s x))) ;; <-- important
      (explain* [_ path via in x] (s/explain s path via in x))
      (gen*  [_ overrides path rmap] (s/gen* s overrides path rmap))
      (with-gen* [_ gfn] (s/with-gen s gfn))
      (describe* [_] (s/describe* s)))))
Run Code Online (Sandbox Code Playgroud)

在您的示例中,您将像这样使用它:

(s/def ::hiccup
  (vector-spec
    (s/cat
      :name       keyword?
      :attributes (s/? map?)
      :contents   (s/* ::contents))))
Run Code Online (Sandbox Code Playgroud)