Fork me on GitHub
#clojure-spec
<
2018-09-13
>
donaldball02:09:55

I’m working on a spec for an asynchronous message format and am curious about versioning. My instinct is to declare a versioned namespace for the message envelope - e.g. :my.message.v1/schema, declare all my envelope fields in that same namespace, and then just make a new one if and when I realize the need to make an incompatible change to the envelope. It occurred to my colleague that multi-spec was another way to accomplish this by including the version as a value, e.g. :my.message.schema/version 1 and using multi-spec to choose the correct spec. Are both of these viable options and is there anything to recommend one over the other?

Alex Miller (Clojure team)03:09:31

I’d recommend not making incompatible changes

Alex Miller (Clojure team)03:09:43

then you don’t need versions :)

👍 12
kappa 8
Alex Miller (Clojure team)03:09:24

if the attribute needs to change, give the attribute a new name

Alex Miller (Clojure team)03:09:27

if you’re talking about adding attributes, that’s compatible. if you need to remove attributes from a map, then you should have a new name for the container, which I guess version is one kind of new name

donaldball14:09:51

Alas, my foresight is sufficiently imperfect that I don’t believe I can develop the correct message format for all time. Incompatible changes I believe might be reasonably desired include (and are probably limited to?): adding and removed required keys.

Alex Miller (Clojure team)14:09:17

well, I’ll refer you to Rich’s Spec-ulation keynote for the long form argument

ghadi14:09:06

what is an example of adding keys that is a breaking change @donaldball?

taylor14:09:41

adding new required keys would be breaking

ghadi14:09:21

important word simple_smile

taylor14:09:29

but yeah I think the gist is "don't make breaking changes" which I generally take to mean "if you need to break something, make a new something, and leave the old something alone"

taylor14:09:59

if you need to add a new required key to some map, it's no longer the same thing. Make a new map spec with a different name that requires the new key

ghadi14:09:49

if it's for a message on something like Kafka, the new name might be an entirely new Topic

donaldball14:09:45

I agree that’s very much the route spec wants you to go down and I’m on board the train, but I am curious what makes the multi-spec idea a poor solution. It seems like it does constrain you forever to having a message be a map, and for the map to require the multi-spec keyword, but otherwise the map contents could vary as required by the versions.

lilactown15:09:18

how do I put a bound on how many times clojure.spec.test.alpha/check generates a test?

lilactown15:09:40

the documentation is very nebulous

taylor15:09:54

@lilactown

(st/check `foo {:clojure.spec.test.check/opts {:num-tests 10}})

❤️ 4
taylor15:09:11

>The opts map includes the following optional keys, where stc aliases clojure.spec.test.check >The ::stc/opts include :num-tests in addition to the keys documented by test.check.

taylor15:09:58

it confused me at first too b/c test.check/quick-check takes the number of tests as its first argument

lilactown15:09:25

yeah it was completely meaningless to me

lilactown15:09:44

hm. even specifying {:num-tests 5}, my generative testing is taking minutes

lilactown15:09:50

in fact, specifying 0 takes minutes? I'm not even sure this is working

taylor15:09:43

this works for me:

(defn foo [x] (str x))
(s/fdef foo :args (s/cat :x any?) :ret string?)
(st/check `foo {:clojure.spec.test.check/opts {:num-tests 1}})
(st/check `foo {:clojure.spec.test.check/opts {:num-tests 100}})

taylor15:09:16

and the :num-tests is accurately reflected in the check result

lilactown15:09:54

I think some of my specs must be really expensive. is there a way for me to selectively narrow some of the child specs?

taylor15:09:11

any recursive specs involved?

taylor15:09:28

hmm hard to say w/o seeing all the relevant code

Alex Miller (Clojure team)15:09:55

Expensive to check or to gen?

lilactown15:09:14

I'm not sure 😞

lilactown16:09:44

I apologize, it's quite long. but I'm not confident what might be what's causing the slowness

lilactown16:09:37

definitely seems to be in the generation of :torch/fields

lilactown16:09:42

I'm going through and removing everything and slowly adding back in. adding back in

:torch.field/key
:torch.field/name
seems to slow it down, but not astronomically

Alex Miller (Clojure team)16:09:59

I meant what operation are you invoking when it’s slow?

Alex Miller (Clojure team)16:09:18

If you have coll-of, it’s good to set :gen-max to something small like 3

lilactown16:09:57

that did help! thanks!

Alex Miller (Clojure team)17:09:08

The default is 20 and that can get a little out of hand

lilactown16:09:30

how do I selectively narrow the values generated by stest/check?

lilactown16:09:28

e.g. if my spec has a named child-spec somewhere that is defined as keyword?, but I don't really care about testing 1000s of keywords

taylor16:09:23

I think you can pass in an override map in check opts :gen

taylor16:09:54

>:gen map from spec names to generator overrides

4