Clojure.spec / unform returning inappropriate values

I did a great job with clojure.spec for the most part. However, I ran into a problem that I could not understand when working with unform. Here is a free spec for Hiccup to make us move:

(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?))

Now, before we leave, let's see if it works with a small passing case.

(def example [:div])

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

;;=> {:name :h1}

It works like a charm. But can we cancel our correspondence?

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

;;=> (:div)

Hmm, this must be a vector. Am I missing something? Let's see what the spec has to say about it.

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

;; val: (:div) fails spec: :user/hiccup predicate: vector?
;;=> nil

In fact, he fails. So the question is: how do I get this to work correctly?

+4
source share
1 answer

, :

(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)))))

:

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

Source: https://habr.com/ru/post/1652482/


All Articles