Fork me on GitHub

I know that you can pull the :ret and :arg spec from a function spec.

(def my-fn-spec
    (s/fspec :args (s/cat :x int?)
             :ret int?))

(s/valid? (:args my-fn-spec) [5])  ;; true
(s/valid? (:ret my-fn-spec) 5)     ;; true
Is there a way to decompose/query other kinds of specs? For example, I would like to do something like the following to get the spec for the individual fn argument named x.
(:x (:args my-fn-spec))
;; or maybe
(first (:args my-fn-spec))


I think the TL;DR is not easily with Spec 1 @erp12 but Spec 2 offers more facilities for taking specs apart and programmatically building them.


In Spec 1, you can get the form of a Spec and break it apart, but it isn't easy to turn that back into Spec objects that you can use tho'...


@seancorfield Good to know, thank you! Based on that, would you agree that currently the best option would be to keep the sub-specs in a map and use some utility functions to "materialize" real specs from them. Just spitballin' here but something like ...

(s/def ::spec (s/spec s/spec?))

; Deconstructed Function Spec
(s/def ::arg-specs (s/coll-of ::spec))
(s/def ::ret-spec ::spec)
(s/def ::d-fn-spec (s/keys :req [::arg-specs ::ret-spec]))

; Deconstructed Collection Spec
(s/def ::coll-kind ::spec)
(s/def ::element-spec ::spec)
(s/def ::d-coll-spec (s/keys :req [::coll-kind ::element-spec]))

; Deconstructed Map Spec
(s/def ::key-spec ::spec)
(s/def ::value-spec ::spec)
(s/def ::d-map-spec (s/keys :req [::key-spec ::value-spec]))

(defn construct-spec 


Or do you know of any other pattens followed by the community for stuff like this?


I don't really understand what problem you are trying to solve here... It doesn't look like the sort of thing I've seen anyone trying to do with Spec.


Have you looked at Spec 2? That's much more amenable to programmatic manipulation of specs...


Hi. I'm struggling with generating data from a simple (s/schema) use case.

(s/def ::x int?)
  (s/def ::baz (s/schema [::x]))
  (s/def ::bar ::baz)
  (s/def ::foo (s/schema [::bar]))
  (gen/sample (s/gen (s/spec ::foo)))


The call to (gen/sample) throws a No implementation of method: :conform* of protocol: #'clojure.alpha.spec.protocols/Spec found for class: clojure.lang.Keyword exception.


While a straightforward translation to s/keys seems to work fine:

(s/def ::x int?)
  (s/def ::baz (s/keys :opt [::x]))
  (s/def ::bar ::baz)
  (s/def ::foo (s/keys :opt [::bar]))

  (gen/sample (s/gen (s/spec ::foo)))
I'm probably getting something wrong in my schema definitions, but I can't figure out the problem.

Alex Miller (Clojure team)20:01:02

code looks fine, prob just a bug


Cool, I'll stick with the (s/keys ..) version for a while.


Changing the :bar definition to

(s/register ::bar (s/resolve-spec ::baz))
appears to work around the issue.

Alex Miller (Clojure team)20:01:06

that makes sense - you're basically copying the spec object rather than relying on resolving through the alias

Alex Miller (Clojure team)20:01:23

I have a pretty good hunch on where that bug is


Awesome, thanks!