Fork me on GitHub
#clojure-spec
<
2022-11-29
>
Ben Lieberman16:11:17

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.

Ben Lieberman16:11:43

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.

pavlosmelissinos17:11:27

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

jkxyz17:11:45

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

👀 1
Ben Lieberman17:11:19

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

pavlosmelissinos17:11:52

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

pavlosmelissinos17:11:42

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

Ben Lieberman17:11:46

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

pavlosmelissinos17:11:25

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

Ben Lieberman17:11:30

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