This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-02-21
Channels
- # announcements (6)
- # bangalore-clj (1)
- # beginners (46)
- # cider (21)
- # cljs-dev (30)
- # cljsjs (3)
- # clojure (131)
- # clojure-dev (20)
- # clojure-europe (2)
- # clojure-italy (12)
- # clojure-nl (11)
- # clojure-russia (4)
- # clojure-spec (55)
- # clojure-uk (57)
- # clojurebridge (1)
- # clojured (1)
- # clojurescript (55)
- # cursive (11)
- # data-science (1)
- # datomic (23)
- # duct (1)
- # emacs (1)
- # events (1)
- # figwheel-main (2)
- # fulcro (219)
- # graphql (16)
- # immutant (1)
- # jackdaw (3)
- # java (6)
- # juxt (30)
- # kaocha (8)
- # mount (3)
- # nyc (1)
- # off-topic (16)
- # pathom (48)
- # pedestal (1)
- # re-frame (71)
- # reagent (17)
- # ring-swagger (3)
- # shadow-cljs (96)
- # spacemacs (21)
- # specter (8)
- # speculative (20)
- # sql (21)
- # test-check (2)
- # tools-deps (12)
- # vim (6)
just like defn-spec adds inlined specs for function return values and parameters, defrecord-spec adds inlined specs for fields, along with adding specs to the built-in record construction functions
Ah. Rich is considering a spec-enhanced defn.
defn and defrecord are the obvious ones to gain spec-enhanced forms. specs for protocol implementations could also be nice (in defrecord/deftype or extend-type). default specs for protocols (in defprotocol)? protocol related stuff I haven’t thought enough about. but have noticed it feels like a “spec gap”
Protocols can’t be spec’ed
@mrevelle if you keep your protocol an implementation detail and dont expose it as your public api it’s not a problem
for example (my-func)
could be a spec’d function and (-my-func)
could be a protocol function
then (my-func)
calls (-my-func)
internally
This is what we did with hoplon
These days we consider that a best practice in the core team
@flyboarder yeah, I suppose. not a fan of the redundancy and lack of documented expectations that specs may provide. it can also be useful to allow others to implement your protocols (see loom and ubergraph)
user> (require '[clojure.spec.alpha :as s])
nil
user> (require '[clojure.spec.test.alpha :as stest])
nil
user> (require '[clojure.spec.gen.alpha :as sgen])
nil
user> (s/def ::one-arg-fn (s/fspec :args (s/cat :x any?)))
:user/one-arg-fn
user> (defn map-vals
"Map the function f over all the values of the associative collection coll."
[f coll]
(reduce-kv (fn [m k v]
(assoc m k (f v)))
(empty coll)
coll))
#'user/map-vals
user> (s/fdef map-vals :args (s/cat :f ::one-arg-fn :coll associative?))
user/map-vals
user> (def xmap {:a " foo" :b "bar "})
#'user/xmap
user> (stest/instrument)
[user/map-vals]
user> (map-vals clojure.string/trim xmap)
Execution error - invalid arguments to user/map-vals at (REPL:65).
(nil) - failed: (apply fn) at: [:f] spec: :user/one-arg-fn
user>
when you use an instrumented fspec, it will actually generate values according to the fspec args spec and invoke the function you pass with them
here you declared that the fspec function takes an any? arg
the generator generated nil and invoked clojure.string/trim with it
which throws
so it's effectively telling you that when passing clojure.string/trim to map-vals, you have passed a function that will not take an any?
or instead of fspec, many people find it easier to just use ifn?
this generative test on instrument of fspec is a feature that many people find surprising (or wrong)
and something we're going to revisit in spec 2
OK. Thanks for the explanation. What would be the recommended way in spec1 to write a spec for a single arity function?
I would just do that
with current state, I don't find that I gain anything but headaches with fspecs
What would be the best way to prepare for spec 2. I now sometimes use the :opt is it useful to rewrite those to :req and make the values also allow nil?
@gklijs s/keys
will stay as-is, as far as I know, so you will be able to move to s/schema
and s/select
piecemeal over time as you need it.
I have our codebase running against spec2 with minimal changes (by depending on the latest git SHA of clojure/spec-alpha2
-- since we use CLI/`deps.edn`) but it's still very much a moving target with no formal release (and still some bugs being worked out).
The main thing you may trip over is if you have used a predicate in a context that is really expecting a spec -- spec1 allows that but spec2 does not. Also, some constructs are now stricter about what constitutes a "spec".
Your best preparation at this point is to read https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha
the fate of s/keys is not yet determined
Oooh... so it may go away before spec2 is "released"?
maybe, but really don't know yet
That would make the migration from spec1 to spec2 a lot more work!
at the moment I'd say it's more likely to still be there
the idea behind schema has shifted a lot since Rich's talk, not sure if it will survive in the form described then
Interesting... I look forward to seeing how this evolves then...
And, yeah, I feel that hammock... I'm still deep in thought about next.jdbc
🙂
@gklijs I would not rewrite opts to nilable reqs - I think that's the wrong direction
really, unless you're a "riding the bleeding edge" guy like Sean, I would just wait
select will have both required and optional selections
although the semantics around optional stuff is a little different
Ok, I'll leave it for now then. I now use it as part of (de)serialisation, with optional added defaults on a subset of what can be specced, also using the spec's to generate the cljs functions to edit the data. I probably need to write some sort of migration at a point, but that should be trivial. Might use another namespace to spec the existing data in spec2 at some point.
If you have two specs but you want to have a shared id between the two, is that possible in a generator? But also the id’s are generated as well. 👀
you'll need custom generator, which probably will have to generate examples for both specs at the same time. Another funky option is to override default id gen to generate predefined small set of ids, and then filter out examples generated by "default" gens which happen to share an id
first option would look like: 1) gen set of ids, 2) gen set of obj1 with default gen 3) gen set of obj2 with default gen 4) override ids in pairs of obj1 obj2 with ids from 1)