Fork me on GitHub
#clojure-spec
<
2019-01-17
>
rickmoynihan18:01:48

Is it possible to (s/merge (s/keys :req-un [:foo/foo]) (s/keys :req-un [:bar/foo])) such that the rightmost stomps on keys to the left? i.e. looking for a way to combine s/keys with merge semantics for merging and precedence

Alex Miller (Clojure team)18:01:17

seems like you want some kind of operation on specs themselves rather than composite specs

rickmoynihan09:01:08

Yes, by stomps on I mean replaces. And yes I’m looking for an operation on specs themselves. Essentially I want two different specs that share the majority of their keys, but have a different spec for one or two keys. If such a thing doesn’t exist I guess I can rework it so the commonanility is together in a shared spec, and merge that with the extra different keys… rather than trying to merge the replacement keys over the top.

rickmoynihan09:01:09

I guess it’s just a little confusing that s/merge has different semantics WRT keys than merge - but I get why s/merge is like this. I guess I’m after an s/merge-keys for merging s/keys specs.

rickmoynihan09:01:30

or maybe s/keys-merge would be a better name.

Alex Miller (Clojure team)13:01:51

Having different semantics for the same key is generally a smell, unless your override is a subset (rather than a different set). I think there may be some new options in spec 2 but I’d say right now I would separate the non-different keys into one spec, then merge with different partial map specs as needed.

rickmoynihan14:01:25

Thanks, I’d already done what you suggest… It’s 3rd party data — so I don’t get to define the keys… but yes the override is actually a subset. Also I’m not sure if this a smell or not; but the superset spec really just exists to represent “noise” that I don’t care about but have to ignore gracefully… I’m not using those specs as specs so much as I’m using the spec API as a way to write generators of noise I need to ignore not barf on. e.g. I have a spec :raw/Body representing all message bodys I should just skip over and a spec :specific/Body for ones I care about. I could use s/or to model this but Body is nested in some other specs e.g. a Message and essentially I don’t want to have to redefine all the intermediates. I think this is essentially similar to the “Maybe Not” problems.

Alex Miller (Clojure team)14:01:08

well you can use s/and now to combine your generic spec and (when necessary) the more restrictive spec

Alex Miller (Clojure team)14:01:23

so it doesn’t replace, but only the more precise thing will pass both

rickmoynihan14:01:08

yeah I did consider that but how do you do so when the spec isn’t the root spec?

Alex Miller (Clojure team)14:01:34

yeah, that’s tricky - you need to rebuild everything back to the root

Alex Miller (Clojure team)14:01:50

this is the kind of thing that will be easier in the spec 2 select stuff

rickmoynihan14:01:01

that was my feeling too

rickmoynihan14:01:08

thanks for confirming my intentions were good, at least! 🙂

rickmoynihan14:01:01

FWIW I have rebuilt to the root… and rearchitectured the commonality into another spec etc… so it’s not so bad this time 🙂

kenny23:01:35

Can you merge a map-of and keys?

taylor23:01:17

for example, this won’t conform if :bar value isn’t a string (or if any of the keys aren’t keywords, or if :foo is missing, etc.):

(s/conform
  (s/merge (s/keys :req-un [::foo])
           (s/map-of keyword? string?))
  {:foo "" :bar ""})

5
taylor13:01:57

Ah, I thought Kenny’s question was independent from yours

rickmoynihan13:01:14

dunno it may have been 🙂