Fork me on GitHub

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


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.


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.


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.


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


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


that was my feeling too


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


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


Can you merge a map-of and keys?


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/merge (s/keys :req-un [::foo])
           (s/map-of keyword? string?))
  {:foo "" :bar ""})


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


dunno it may have been 🙂