Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Spec by
The example belows shows a case where a conform-ed form, does not conform any after an unform. It would be my expectation that you can repeat conform -> unform -> conform endlessly and get the same result.


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

(s/def ::defn-macro (s/cat :type #{'defn} :definition :clojure.core.specs/defn-args))

(let [form '(defn foo "bar" ([a & b] a a c) ([a b] a))]

  (-> form
      (->> (s/conform ::defn-macro))) ;;=> {:type defn, :definition {:name foo, :docstring "bar", :bs [:arity-n {:bodies [{:args {:args [[:sym a]], :varargs {:amp &, :form [:sym b]}}, :body [a a c]} {:args {:args [[:sym a] [:sym b]]}, :body [a]}]}]}}

  ;; Unforming returns the function definition, but with the args in a list instead of a vector:
  (->> form
       (s/conform ::defn-macro)
       (s/unform ::defn-macro))  ;;=> (defn foo "bar" ((a (& b)) a a c) ((a b) a)))

  ;; Conforming after unforming doesn't work anymore
  (->> form
       (s/conform ::defn-macro)
       (s/unform ::defn-macro)
       (s/conform ::defn-macro)) ;;=> :clojure.spec/invalid

    )

7 Answers

+1 vote
by

For the specific case of "regex coll that must also be a vector", it's not really possible to achieve this with spec 1 and make it work for all of conform, unform, and gen.

In spec 2, we have added support for non-flowing s/and (currently called s/and- but that name is still in flux, might end up as s/union or something else), which is needed to add s/cat variants that additionally enforce the coll is either a vector or sequence as s/catv and s/cats.

Eventually we will rework the core.specs using spec2 and use s/catv for the defn arg vector which will resolve this issue.

0 votes
by

Comment made by: jeroen

This gist shows the above code with better formatting https://gist.github.com/jeroenvandijk/28c6cdd867dbc9889565dca92673a531

0 votes
by

Comment made by: lgs32a

This can quickly be traced down to :clojure.core.specs/arg-list which is speced as a (s/and vector?). When unforming, it doesn't create a vector.

Thinking about it, a vcat would be nice for this and similar cases.

0 votes
by

Comment made by: marco.m

Hello, any news ?

0 votes
by

Comment made by: alexmiller

Nope.

0 votes
by

Comment made by: jeroen

Found another issue:

(->> '(foo (link: h0 & [h1)])
(s/conform :clojure.core.specs.alpha/defn-args)
(s/unform :clojure.core.specs.alpha/defn-args))
;#=> '(foo (link: h0 (& [h1))])

Here the rest arguments are wrapped in an extra list. Where should I report this?

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2021 (reported by jeroen)
...