Fork me on GitHub
#clojure-spec
<
2018-07-02
>
ackerleytng00:07:05

s/gen allows you to override a specific generator?

ackerleytng00:07:58

I see, thanks!

otfrom09:07:23

I have a map where one of the namespaced keys ::foo for example, in the whole program can be one of 3 values #{"foo" "bar" "baz"}, but in one part of the program can only be #{"foo" "bar"} because I've filtered out "baz". Is there a way of expressing this in spec? I've been looking around and I haven't found anything (might be morning brane and solved by more coffee, but I'm not sure)

rickmoynihan11:07:57

Interesting… I think this is awkward because it’s expressing something that’s brittle and contrary to the growth ideals of spec? i.e. saying “this set must have only these keys” is analogous to saying “this map must have these keys and no others”. The solution with maps in this situation is often to use select-keys in your function, so the function is robust to inputs with arbitrary other keys as it will ignore them. Could the solution in your situation be to intersect the set-argument with #{"foo" "bar"} so anything else is ignored?

otfrom15:07:08

hmm.. I hadn't thought about the growth ideals properly when thinking about this. It makes me think that the envelope should be very permissive and that I should create new maps for the payload with new specs going through the rest of the system. I think that is why I keep asking these questions as spec affords some forms and punishes others (for good reasons such as growth), but it can be difficult to see it all the time and design accordingly.

rickmoynihan07:07:06

Yes, if you build specs where you find yourself trying to spec “and nothing else” you’ll struggle. Obviously I don’t really know what you’re doing, but your new ideas seem to make perfect sense from a general architectural perspective i.e. the transport layer of an architecture typically shouldn’t know about specifics of the application/content layer.

otfrom08:07:12

I often find if I'm struggling against one of the affordances of something in clojure it is because I'm not thinking about it very well. I think this might be one of those cases. Thx!

👍 8
carocad14:07:43

@U0525KG62 I have had that case as well though not for part of the program but rather for part of an api response. My approach was to split the set into the two possible solutions and then use s/or and s/merge to express the parts that are common and differentiate the other ones

andre.stylianos10:07:20

Depends on what you want out of it I think, and what those things mean in your code. You can make ::filtered-foo which is #{"foo" "bar"} and ::foo is now s/or of ::filtered-foo or #{"baz"}.

andre.stylianos10:07:21

Just avoid having ::foo itself mean two different things depending on context, as that breaks specs being globally consistent

otfrom13:07:50

hmm... makes me want to use unqualified keys for things like that then

otfrom13:07:13

I basically have a lot of "type fields" and often filter down to particular types in certain parts of the system and would like to constrain things in those sections (esp things like generative testing)

otfrom13:07:53

and different affordances drive different design styles in maps

andre.stylianos13:07:12

You could also leave ::foo as being #{"foo" "bar" "baz"} and in specific places just say that ::foo.subset is (s/and #(not= % "baz") ::foo)

otfrom14:07:27

yeah, it just means that the key has to change going through so any code that does work on the supertype wouldn't be pointing at the right key.

otfrom14:07:32

it just makes an envelope and payload style tricky to do (as payload might have loads of types)

otfrom14:07:31

so this is tricky to model in spec then

{::id <some uuid>
 ::timestamp <some timestamp>
 ::payload <a number of different things that are maps that vary depending on domain>}

rickmoynihan11:07:21

isn’t this the use case for a multi-spec on ::payload?

otfrom14:07:05

if you want to be able to constrain ::payload later as you are only dealing with one of the domains.

otfrom14:07:07

I suppose you could leave ::payload as just a map

otfrom14:07:20

and deal with the payload maps separately

andre.stylianos14:07:54

Well, you can either have ::domain-1/payload, ::domain-2/payload and so on, or you could spec ::payload with s/or, conform it and check if the conformed value is the branch you expected

andre.stylianos14:07:22

do keep in mind that I'm no clojure.spec expert, just brainstorming a bit :man-shrugging:

otfrom14:07:11

or have a non-namespaced :payload and define it as :req-un as needed

seancorfield14:07:47

Well, you can use namespaced variants of :xxx/payload and still define it as :req-un -- that way you can see which version you're working with -- and still just use :payload in the map.

4
otfrom15:07:44

I'm not sure I understand you @seancorfield. Does that give me the ability to say "this bit of code here only works with this domain of what might go in payload rather than all the domains"

seancorfield15:07:18

You can have multiple specs with the same set of (unqualified) keys -- but each spec can use different definitions for those keys by using different qualified versions of a key.

seancorfield15:07:03

(s/keys :req-un [:foo/bar]) and (s/keys :req-un [:quux/bar]) -- the actual map has :bar in both cases but the specs are distinct.

otfrom15:07:31

cool. That was what I thought. That you could basically redefine what the *un*qualified keys meant where you wanted, but that you couldn't do that with qualified keys in a map. I didn't know about the :req-un sugar you had their tho (or had forgotten)

guy15:07:47

I mean you still have to s/def the keys right

guy15:07:13

(s/def :quux/bar string?) and (s/def :foo/bar pos-int?) for example

guy15:07:26

u can’t just use :req-un to redefine them

guy15:07:33

i’m pretty sure

otfrom15:07:47

yeah, but I can do that per domain :domain1/bar :domain2/bar

guy15:07:52

ye :thumbsup:

guy15:07:02

>That you could basically redefine what the *un*qualified keys meant where you wanted, Just wasn’t sure what u meant here

otfrom15:07:08

which is exactly what I want to do here

guy15:07:12

cool cool

guy15:07:13

:thumbsup:

otfrom15:07:26

feels like a bit of a cheat, but it does work with what spec affords

otfrom15:07:34

and I'm all about the affordances

guy15:07:37

it’s in the spec guide if it makes you feel better 😄

guy15:07:52

ya i believe so

guy15:07:13

scroll a bit down

guy15:07:23

Much existing Clojure code does not use maps with namespaced keys and so keys can also specify :req-un and :opt-un for required and optional unqualified keys. These variants specify namespaced keys used to find their specification, but the map only checks for the unqualified version of the keys.

otfrom15:07:08

hmm.. I hadn't thought about the growth ideals properly when thinking about this. It makes me think that the envelope should be very permissive and that I should create new maps for the payload with new specs going through the rest of the system. I think that is why I keep asking these questions as spec affords some forms and punishes others (for good reasons such as growth), but it can be difficult to see it all the time and design accordingly.