Fork me on GitHub
#clojure-spec
<
2018-09-05
>
djtango09:09:48

^ makes sense. Thanks for the explanations @seancorfield @alexmiller

ikitommi12:09:02

cross-posting here too: a sample app with clojure.spec & ring, with conforming-based coercion: https://github.com/metosin/reitit/blob/master/examples/ring-spec-swagger/src/example/server.clj

ikitommi12:09:52

in the example, the ::x and ::y specs are coerced either from strings (the get-endpoint with query-parameters) or from json (the post-endpoint if client sends json), or just validated (the post-endpoint if client sends edn or transit).

Chris13:09:06

Is s/fspec the proper way to define a spec for a higher order function? When I declare something with s/fspec and then try to generate a value, it appears to be called many times (perhaps by conform?) is there a way to declare a higher order function without it being invoked with conform? Example: `(s/def ::post-download-fn (s/fspec :args (s/cat :size nat-int?) :ret any? :gen (fn [] (gen/return (fn post-dl-fn [size] (log/info (str "Post Download fn called! n: " size)))))))` (gen/generate (s/gen ::post-download-fn)) Gives a bunch of these before returning the single value generated:

Post Download fn called! n: 0
Post Download fn called! n: 1
Post Download fn called! n: 1
....
Am I missing something obvious?

Chris14:09:56

I believe it to be instrumentation validating conformance - but it's odd that we can't turn it off in the case of a side effecting higher order function.

taylor14:09:39

@chris547 there might be a better way, but there's a *fspec-iterations* dynamic binding you can use to sidestep that behavior:

(defn foo [f] (f 1))
(s/fdef foo :args (s/cat :f (s/fspec :args (s/tuple int?))))
(st/instrument `foo)
(binding [clojure.spec.alpha/*fspec-iterations* 0]
  (foo #(doto % prn inc)))

Chris14:09:50

Oh great, I wasn't aware of that binding - thanks @taylor!

taylor14:09:28

np, I'm sure there's better advice/guidance on how to handle this for higher-order functions that take side-effecting functions, and I'm sure it's something that must be considered as clojure.core gets specs. One obvious workaround is to sacrifice specificity by just using e.g. ifn? as a predicate

basti19:09:04

Hi, is it possible to nest specs, rather than creating for each tiny bit a new s/def ? So an inline version of e.g.

(s/def ::key1 string?)
(s/def ::my-map (s-keys [::key1]))
?

taylor19:09:23

for s/keys specs, you need registered specs for the keywords. For other non-`s/keys` types of specs, you can nest spec definitions

basti19:09:22

k thanks for the info - appreciated! how would that look like for that the spec above?

taylor19:09:40

(s/def ::my-map (s/keys :req [::key1]))

taylor19:09:16

or :req-un for unqualified keywords

basti19:09:57

Oh I rather meant to define the spec for ::key1 in the ::my-map definition. I don’t want to reuse ::key1

taylor19:09:23

I don't think that's possible

taylor19:09:50

not with an s/keys spec, at least

crs19:09:16

Do you know if there is a way for a recursive spec to nest uniquely? So the same tag can’t repeat across a branch e.g. NO

[:paragraph
 [:paragraph "foo"]]
or
[:link
 [:link "foo"]]
or
[:italic
 [:bold
  [:italic "foo"]]]
but repeating siblings are fine
[:paragraph
 [:bold "foo"]
 [:bold" "bar"]]
Here’s the spec I’m currently using
(s/def ::attrs (s/map-of #{:url} string? :gen-max 2))
(s/def ::hiccup (s/or :string  string?
                      :element (s/cat :tag #{:paragraph :link :bold}
                                      :attrs (s/? ::attrs)
                                      :content (s/* ::hiccup))))

Alex Miller (Clojure team)19:09:50

Generally, I would say no

Alex Miller (Clojure team)19:09:42

You can of course write any arbitrary predicate you want and include it

crs19:09:05

Thanks. I’ll give that a try