clojure-spec

liebs 2022-11-29T16:51:17.928799Z

What is the most idiomatic way to spec "if this key exists in a map, that key should not exist?" I'm finding this a bit more confusing than I initially expected. A short example:

;; map with either short or long name but not both
(def a-map {:short-name "foo"}) ; should be valid
(def a-bad-map {:short-name "foo" :long-name "foobar"}) ; should be invalid
I've tried various combinations of s/and , not and other core fn's without luck.

liebs 2022-11-29T16:59:43.075189Z

my latest try, for example, blows up the stack:

(s/def ::short-route-name (s/and string? #(s/invalid? (s/conform ::long-route-name %))))
(s/def ::long-route-name (s/and string? #(s/invalid? (s/conform ::short-route-name %))))
I also tried using or but that fails too.

pavlosmelissinos 2022-11-29T17:05:27.741209Z

spec favors open maps by design; it doesn't really support statements like "this key shouldn't exist"

jkxyz 2022-11-29T17:06:45.276429Z

You can use a predicate like #(not (and (:foo %) (:bar %)))

👀 1
liebs 2022-11-29T17:07:19.872209Z

I saw that Malli has both an enum spec and an option to close maps but I want to learn spec before I give Malli a try, even though both those features would be useful to me here

pavlosmelissinos 2022-11-29T17:11:52.765619Z

There are also third party libraries, like https://github.com/bhauman/spell-spec that "close" specs if you really want that functionality.

pavlosmelissinos 2022-11-29T17:12:42.023169Z

I wouldn't try to model "negative" specs from within spec, it's not designed for that.

liebs 2022-11-29T17:13:46.211719Z

Well I guess I feel slightly less silly then for finding this pretty non-obvious

pavlosmelissinos 2022-11-29T17:14:25.084079Z

An interesting approach is that followed by cognitect's aws-api; instead of saying "this is what a valid response doesn't look like", it specs anomalies: https://github.com/cognitect-labs/anomalies

liebs 2022-11-29T17:18:30.886739Z

I'll have to check that out, thanks @pavlos