clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
Eddie 2020-01-02T03:16:17.052800Z

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

seancorfield 2020-01-02T03:20:20.054Z

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.

seancorfield 2020-01-02T03:25:21.055Z

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'...

Eddie 2020-01-02T03:42:39.055900Z

@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 
  [m] 
  ...)

Eddie 2020-01-02T03:43:37.056900Z

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

seancorfield 2020-01-02T03:59:37.058100Z

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.

seancorfield 2020-01-02T04:00:20.058600Z

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

rafael 2020-01-02T20:10:16.061900Z

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

rafael 2020-01-02T20:10:57.062500Z

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.

rafael 2020-01-02T20:12:40.063900Z

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.

alexmiller 2020-01-02T20:32:02.064200Z

code looks fine, prob just a bug

alexmiller 2020-01-02T20:32:15.064400Z

in spec

rafael 2020-01-02T20:38:02.065100Z

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

rafael 2020-01-02T20:43:12.066500Z

Changing the :bar definition to

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

alexmiller 2020-01-02T20:48:06.066900Z

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

alexmiller 2020-01-02T20:48:23.067200Z

I have a pretty good hunch on where that bug is

rafael 2020-01-02T20:57:18.067400Z

Awesome, thanks!