Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing with clojure.spec

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

consider the following data:

(def data {:names [["Anna"      :lucky]
                   ["Peter"]
                   ["Jon"       :lucky]
                   ["Andre"     :lucky]]})

It's a hash-map of with one key :names having a value of a vector of vectors. The inner vectors must contain a string as the first element and can optionally contain a the keyword :lucky as the second element.

The previous two sentences should be described with clojure.spec- Let's start with the items in the vector:

(s/def ::item (s/cat :name string? :lucky (s/? #(= :lucky %))))

(s/conform ::item ["Tom"])
;; {:name "Tom"}
(s/conform ::item ["Tom" :lucky])
;; {:name "Tom", :lucky :lucky}
(s/conform ::item ["Tom" :sad])
;; :clojure.spec/invalid

This works. However, if there is only one option. Wouldn't a parsed result look be better like this:

`{:name "Tom", :lucky true}` or `{:name "Tom", :lucky false}`

Can this be done in clojure.spec?

With this, one can carry on:

(s/def ::items (s/coll-of ::item '()))

(s/conform ::items [["Tom" :lucky] ["Tim"]])
[["Tom" :lucky] ["Tim"]]

However, it looks like it passes the test but why the items are not parsed anymore?

Edit: This could be solved by switching from alpha7 to alpha10 release, where coll-of only takes one argument

Finally my spec looks like, having the previously described caveats:

(s/def ::my-spec (s/keys req-un [::items]))
like image 538
Anton Harald Avatar asked Oct 29 '22 21:10

Anton Harald


1 Answers

spec is not designed to provide arbitrary data transformation (Clojure's core library can be used for that).

It is possible to achieve this using s/conformer but it is not recommended to use that feature for arbitrary transformations like this (it is better suited to things like building custom specs).

like image 63
Alex Miller Avatar answered Jan 02 '23 20:01

Alex Miller