Fork me on GitHub
#clojure-spec
<
2019-02-21
>
drone00:02:00

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

drone00:02:35

eh, s/inlined/inplace

alexmiller00:02:01

Ah. Rich is considering a spec-enhanced defn.

drone00:02:24

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”

alexmiller00:02:56

Protocols can’t be spec’ed

flyboarder01:02:35

@mrevelle if you keep your protocol an implementation detail and dont expose it as your public api it’s not a problem

flyboarder01:02:46

for example (my-func) could be a spec’d function and (-my-func) could be a protocol function

flyboarder01:02:00

then (my-func) calls (-my-func) internally

flyboarder01:02:22

This is what we did with hoplon

alexmiller01:02:55

These days we consider that a best practice in the core team

drone03:02:40

protocols could be spec’d

drone03:02:58

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

dorab03:02:40

What am I doing wrong here?

dorab03:02:47

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> 

alexmiller04:02:06

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

alexmiller04:02:19

here you declared that the fspec function takes an any? arg

alexmiller04:02:32

the generator generated nil and invoked clojure.string/trim with it

alexmiller04:02:40

which throws

dorab04:02:03

Ah. Thanks. So, I should use a s/with-gen in the spec definition of ::one-arg-fn?

alexmiller04:02:05

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?

alexmiller04:02:38

or instead of fspec, many people find it easier to just use ifn?

alexmiller04:02:00

this generative test on instrument of fspec is a feature that many people find surprising (or wrong)

alexmiller04:02:15

and something we're going to revisit in spec 2

dorab04:02:25

OK. Thanks for the explanation. What would be the recommended way in spec1 to write a spec for a single arity function?

dorab04:02:17

Or, is the recommendation to just use ifn? as you mention above?

alexmiller04:02:25

I would just do that

alexmiller04:02:48

with current state, I don't find that I gain anything but headaches with fspecs

gklijs05:02:13

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?

seancorfield06:02:29

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

seancorfield06:02:43

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

seancorfield06:02:05

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

alexmiller06:02:10

the fate of s/keys is not yet determined

seancorfield06:02:35

Oooh... so it may go away before spec2 is "released"?

alexmiller06:02:22

maybe, but really don't know yet

seancorfield06:02:22

That would make the migration from spec1 to spec2 a lot more work!

alexmiller06:02:37

at the moment I'd say it's more likely to still be there

alexmiller06:02:28

the idea behind schema has shifted a lot since Rich's talk, not sure if it will survive in the form described then

dafuq 2
alexmiller06:02:44

he's deep in the hammock :)

🌴 3
clj 1
seancorfield06:02:59

Interesting... I look forward to seeing how this evolves then...

seancorfield06:02:16

And, yeah, I feel that hammock... I'm still deep in thought about next.jdbc 🙂

alexmiller06:02:20

@gklijs I would not rewrite opts to nilable reqs - I think that's the wrong direction

alexmiller06:02:44

really, unless you're a "riding the bleeding edge" guy like Sean, I would just wait

alexmiller06:02:54

select will have both required and optional selections

alexmiller06:02:29

although the semantics around optional stuff is a little different

gklijs06:02:17

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.

guy15:02:48

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

borkdude15:02:58

example please?