I've been getting on quite well with clojure.spec for the most part. However, I came to a problem that I couldn't figure out when dealing with unform
. Here's a loose spec for Hiccup to get us moving:
(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 get carried away, let's see if it works with a small passing case.
(def example [:div])
(->> example
(s/conform ::hiccup))
;;=> {:name :h1}
Works like a charm. But can we then undo our conformance?
(->> example
(s/conform ::hiccup)
(s/unform ::hiccup))
;;=> (:div)
Hmm, that should be a vector. Am I missing something? Let's see what spec has to say about this.
(->> example
(s/conform ::hiccup)
(s/unform ::hiccup)
(s/explain ::hiccup))
;; val: (:div) fails spec: :user/hiccup predicate: vector?
;;=> nil
Indeed, it fails. So the question: How do I get this to work correctly?
Late response here. I had not realised how old this question was but since I had already written a reply I might as well submit it.
Taking the spec that you provide for ::hiccup
:
(s/def ::hiccup
(s/and
vector?
(s/cat
:name keyword?
:attributes (s/? map?)
:contents (s/* ::contents))))
The vector?
spec within and
will test the input data against that predicate. Unluckily as you have experienced, it doesn't unform to a vector.
Something that you can do to remedy this is to add an intermediate spec that will act as the identity when conforming and will act as desired when unforming. E.g.
(s/def ::hiccup
(s/and vector?
(s/conformer vec vec)
(s/cat
:name keyword?
:attributes (s/? map?)
:contents (s/* ::contents))))
(s/unform ::hiccup (s/conform ::hiccup [:div]))
;;=> [:div]
(s/conformer vec vec)
Will be a no-op for everything that satisfies vector?
and using vec
as the unforming function in the conformer will make sure that the result to unforming the whole and
spec stays a vector.
I am new to spec myself and this was how I got it working as you intend. Bear in mind it might not be the way in which it was intended to be used by spec's designers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With