Fork me on GitHub

I'm trying to write a util that prints out a more friendly error message based on the data returned from s/explain-data. I'm noticing that :in contains misleading data for certain cases:

user=> (s/explain-data (s/map-of integer? integer?) {345 "a"})
{:clojure.spec.alpha/problems ({:in [345 1],
                                :path [1],
                                :pred clojure.core/integer?,
                                :val "a",
                                :via []}),
 :clojure.spec.alpha/spec #<[email protected]>,
 :clojure.spec.alpha/value {345 "a"}}
user=> (s/explain-data (s/coll-of integer? :kind set?) #{1 2 "a"})
{:clojure.spec.alpha/problems ({:in [1],
                                :path [],
                                :pred integer?,
                                :val "a",
                                :via []}),
 :clojure.spec.alpha/spec #<clojure.spec.alpha$every_impl$r[email protected]>,
 :clojure.spec.alpha/value #{1 2 "a"}}
So for a map-of, it is giving me the key as well as 1 (to indicate the val of the map pair), and for a set, it is giving me an index into some arbitrary ordered version of that set. Neither of these would work if I tried to get-in on those paths. If these values were somehow prefaced with an indicator of what special case triggered them, I could work with that, but the fact that the data may or may not be misleading with no additional context makes the overall data unhelpful for writing a universal tool. Is that a bug? Or is there another way of introspecting the failed specs that I'm missing?

Alex Miller (Clojure team)01:01:30

on the first case, this is a side effect of treating maps as a sequence of tuples. there is at least one ticket about this particular one with a pending patch.

Alex Miller (Clojure team)01:01:48

for the set case, I don’t think anything is logged but a ticket for that would be fine


Thanks, I'll try to find time to write up a ticket. I'll definitely check out the other ticket you mentioned for map-of, since I'm curious what the proposed solution was, so I know what the expected behavior should be for sets.


I ended up writing a lot of code to disambiguate these cases in Expound. . The ticket for map-of is .


IIRC, takes a different approach to the problem: it analyzed the specs directly to help understand the explain-data (I could be mis-stating something here, @U0508956F please correct me if I’m wrong 🙂 )


@U08EKSQMS thanks! do you have any specific thoughts on the set issue as well? are you also handling that specially from expound?


I haven’t traced the execution too closely, but I suspect that the set case is handled by this code


This could have bugs though. It took awhile to figure out an approach that would work for most :in paths that I could find.


Re: sets, I would vote for any JIRA ticket that makes :in paths non-ambiguous, because I think this is one of the biggest hurdles with building pretty-printers for spec. If you make an issue for the set case, send me the link 😄


Thanks @U08EKSQMS, I’ve been meaning to take a closer look at Expound actually


Cool! Feedback is welcome and appreciated


@aengelberg I’d like to see that too if you find it.


is it overkill?

(s/nonconforming (s/or :i pos-int? :z #{0}))


- zero? throws on strings, keywords, etc. - just (complement neg-int?) accepts everything but neg ints: strings, maps, etc.


is this in any "under-the-hood" way lighter?

(s/and int? (complement neg-int?))


missed it opieop


Given the following data: {:a {:name "a"} :b {:name "b"} :c {:name "not-c"}}, how could I construct a spec to say "the key to a value-map must be the keywordized name within the value-map"? This spec would complain that (keyword "not-c") does not match :c

Alex Miller (Clojure team)21:01:49

You can use any arbitrary predicate like #(= (keys %) (->> % vals (map :name) (map keyword))) to check validity

Alex Miller (Clojure team)21:01:11

but it’s not going to give you that specific error


great thank you