Fork me on GitHub
James Vickers01:11:04

What is the shape of the data that gets passed to the :fn argument of s/fdef? Does someone have an example of that (or a way to print it)?


it’s a map with :ret and :args keys


the :args value is the conformed value of the args I think


:fn (fn [{:keys [args ret]}]

James Vickers01:11:45

Thanks. Is that in the docs somewhere?

Alex Miller (Clojure team)01:11:52

That is all correct - the values of the map are the confirmed values of the args and ret specs

Alex Miller (Clojure team)01:11:25

I’m not sure where it would be doc’ed in the docstrings


:fn A spec of the relationship between args and ret - the
  value passed is {:args conformed-args :ret conformed-ret} and is
  expected to contain predicates that relate those values


fdef docstring

James Vickers01:11:41

So, if the function returns a single value (like a number), then (% :ret) in the :fn spec should yield the return value?

Alex Miller (Clojure team)01:11:41

Depends on the ret spec


it might be tagged/conformed though I guess?

Alex Miller (Clojure team)01:11:52

If it’s a simple pred then yes

James Vickers01:11:16

Sweet, thanks.

James Vickers01:11:48

I was surprised that putting a do expression with a println in the :fn spec didn't print anything when the function was called (instrumentation on)


the :fn spec doesn’t come into play re: instrument calls


it does get used if you check the function though


> Instrumentation validates that the :args spec is being invoked on instrumented functions …


> check will generate arguments based on the :args spec for a function, invoke the function, and check that the :ret and :fn specs were satisfied.

James Vickers01:11:55

Cool. I ran a check and it printed out what was passed to :fn.


If I have a production spec of say an email address, it will require a custom generator if I want to generate test data. Since a generator typically will not be used in production would it be a good practice to redefine the production spec in i test ns that will include the generator, keeping any generators out of production code? Can this lead to problems if I have nesting and to be able to generate the top level spec I need to redefine lower levels with the generator in my tests?

Alex Miller (Clojure team)14:11:18

Another thing to consider is using generator overrides at the point of testing. That way you don’t have to redefine a spec, you just supply a set of alternate generators.


But in the case of a nested spec such as a person having an email, and I am testing the person don't I have to redefine the email spec refered by the person spec? Is generator overriding done by using with-gen or are there other ways I have missed?

Alex Miller (Clojure team)14:11:45

no, you can supply custom generators in exercise, instrument, check, etc

Alex Miller (Clojure team)14:11:03

in stest/check for example, you can supply a :gen map in the options: “map from spec names to generator overrides”

Alex Miller (Clojure team)14:11:27

so that way you can supply overrides just in the context of a test


ah I see, will try that out, thanks for your help!


Good to be reminded of that. So far, we've tended to write wrappers for generators so we can lazy load the testing library (and therefore keep the actual "testing generator" in the production code without needing the testing libraries in production).


hello, has anyone here though about the idea of specing nested structures? currently s/keys only supports flat structures, if I need a nested I have to define the nested structure ahead of time on that key, this prevents different nesting structures depending on the context


maybe we could support via a syntax like the datomic pull syntax, eg: (s/keys :req [:user/name {:user/address [:address/line1 :address/city]}])


@U066U8JQJ have you checked ? nests also vectors and sets. In the end, just generates specs with alternative (macro free) syntax.


also would like to see support for nested keys-specs in spec itself.

Alex Miller (Clojure team)14:11:51

that’s not in line with the “set of attribute” thinking and no plans for that


the issue I'm facing is that for the "container" specs I might want different subsets of keys, depending on the context


so by not having a fixed for the children, it gets tricky

Alex Miller (Clojure team)15:11:57

you don’t need a fixed spec for the children

Alex Miller (Clojure team)15:11:10

an empty (s/keys) is sufficient to cover an open set of attributes

Alex Miller (Clojure team)15:11:22

or use s/multi-spec to select the spec based on the contents

Alex Miller (Clojure team)15:11:30

or s/or multiple choices, etc


thanks for the tips Alex, I like the open one, but at same time, if I want to require different sets of keys depending on the context, that doesn't work, the multi-spec can work, but its a lot more involved


I love the idea of living by just attributes, but if I need to give a name to a context of sub-attributes, I feel like backing again to the "box" (class, entity, whatever...) constraints again


@U055NJ5CC thanks for pointing that out, I'll look it up

Alex Miller (Clojure team)15:11:17

what you’re saying is that they key at the top level does not have a stable semantic meaning so I would think about what that means and whether it’s a good idea


yeah, in general terms, any sub-set might be unstable, if what you care is just about the leaf attributes validation


I've been writing a considerable amount of code regarding to data fetching apis ( style), and many times I see that the sub-set of keys of a child element can vary wildly, in my case I'm embracing this and it's working pretty good, but I can't get the specs around to match it in the way it is


the problem is the children, for example, working in micro-service architecture


I have many endpoints across services that can return different sub-sets of the data


and altough they share some root keys, what comes in the children is variable, some endpoints give more, some give less information


and in current spec way, I can't define what is required for each return (or input) on a case-to-case bases


in the same way we can't have a good definition of what are the required fields for an user (a login might be user/password, a signup would require much more)


I feel the same problem when trying to specify requirements for a nested item

Alex Miller (Clojure team)15:11:30

I think that’s all ok, and you should not try to define every key set aggregation


but that's the problem, if I have one key like :user/address, I expect this key to be the same always, but what I expected to be inside of it can change from case to case


on the top level, just create a new set and we are all good


for the nested, there is no way to override the requirements


to say that the sub-set is different a separated case, makes sense?

Alex Miller (Clojure team)15:11:19

why not just (s/def :user/address (s/keys)) ?

Alex Miller (Clojure team)15:11:35

then rely on your address attribute specs to do the work


the only issue there is that we can't make some attributes required for a context that way


so the validation gets too loose

Alex Miller (Clojure team)15:11:08

if you need that then it sounds like s/multi-spec to me, or state all the possibilities with s/or, or add outer constraints s/and’ed at the top level


humm, the top level constraint sounds like a good path for the cases I'm thinking


thanks, I'll try that and see how it goes

Alex Miller (Clojure team)15:11:56

always come back to “state the truth about what it’s in your data” - what can occur in your actual data? say that in the spec.


had anyone felt the desire to specify the kinds on that way?