For stuff like this s/merge is probably preferable to s/and (when combining map specs) - the difference being that merge does not flow conformed results, will combine all failures, and that gen can work better in some cases.
(s/def ::a int?) (s/def ::b string?) ;; changed for this example (s/explain ::my-map {::a 1 ::b 2 ::BAD 3}) In: [:user/b] val: 2 fails spec: :user/b at: [:user/b] predicate: string? ;; vs: (s/def ::my-map2 (s/merge (s/keys :req [::a ::b]) (s/map-of #{::a ::b} any?))) (s/explain ::my-map2 {::a 1 ::b 2 ::BAD 3}) In: [:user/b] val: 2 fails spec: :user/b at: [:user/b] predicate: string? In: [:user/BAD 0] val: :user/BAD fails spec: :user/my-map2 at: [0] predicate: #{:user/a :user/b} ^^ Note you get *both* failures here - both bad attribute value AND the invalid key vs the prior one where you only get the first failure. On Tuesday, September 20, 2016 at 11:38:47 AM UTC-5, Beau Fabry wrote: > > boot.user=> (s/def ::my-map (s/and (s/keys :req [::a ::b]) (s/map-of > #{::a ::b} any?))) > boot.user=> (s/explain ::my-map {::a 1 ::b 2 ::BAD 3}) > In: [:boot.user/BAD 0] val: :boot.user/BAD fails spec: :boot.user/my-map > at: [0] predicate: #{:boot.user/a :boot.user/b} > > Seems better > > On Tuesday, September 20, 2016 at 5:38:10 AM UTC-7, David Goldfarb wrote: >> >> In clojure.spec, how can I declare a map that accepts only certain keys? >> >> *{::a 1 ::b 2 ::BAD 3}* does conform to *(s/keys :req :req [::a ::b])*, >> but I want a spec that will be bothered by ::BAD or any other undeclared >> key. >> >> My use case: I am introducing spec to some legacy code, and I want to be >> warned if I have failed to specify some elements that may appear in my map. >> >> >> Question 2: >> >> So, assuming that this is not possible currently, I brute-forced it with: >> >> *(defn- key-checker [valid-keys]* >> * (fn [map-to-check]* >> * (empty? (clojure.set/difference (into #{} (keys map-to-check)) >> valid-keys))))* >> >> *(s/def ::my-map (s/and (s/keys :req [::a ::b]) (key-checker #{::a >> ::b})))* >> >> >> Ignoring the ugly, and easily fixable, smell of the duplicated set of >> keys, this has a bigger problem: >> >> If the predicate fails, the error that assert gives me is *"{... big >> ugly map ...} fails predicate: (key-checker #{::a ::b})"* with no easy >> way for the viewer to see which key failed. Can I somehow hook into the >> explain mechanism to give a more useful message? >> > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.