Fork me on GitHub
#clojure-spec
<
2022-02-16
>
Colin P. Hill15:02:50

Is there any way to spec a map that can mostly be described with map-of, but has a few special keys with their own types? E.g.

{::special-1 {:I-should-be :a-map}
 ::special-2 :I-should-be-a-keyword
 "standard-key-1" "standard-val-1"
 "standard-key-2" "standard-val-2"
 ; ...
 "standard-key-n" "standard-val-n"}

Colin P. Hill15:02:46

(This is a structure already in use that I'm speccing for documentation purposes. I wouldn't generally design something that functions both as a record and as a map of arbitrary well-typed values.)

kenny15:02:49

Does s/merge a s/map-of and s/keys help?

Colin P. Hill15:02:00

That was my first thought, but I'm not seeing a way to write the map-of part. If I did (map-of string? string?), the special keys and values would fail validation.

Alex Miller (Clojure team)15:02:53

yes, you can do this by spec'ing it as a collection of kv pairs and then describe the pairs. I have a blog post about the destructuring spec at https://cognitect.com/blog/2017/1/3/spec-destructuring that has this characteristic

thanks3 1
Alex Miller (Clojure team)15:02:57

the key is you want (s/every (s/or ...kv-tuple-shapes...) :into {})

thanks3 1
Colin P. Hill15:02:30

Hmm, I've been defaulting to using coll-of rather than every, but I'm seeing that it blows up when I try to do that with :into {}

Colin P. Hill16:02:10

(s/conform (s/coll-of (s/cat :k string? :v string?) :into {}) {"foo" "bar" "baz" "bork"})

Execution error (Error) at (<cljs repl>:1).
nth not supported on this type cljs.core/PersistentArrayMap
=> :repl/exception!
Could be a quirk of the cljs implementation

Alex Miller (Clojure team)16:02:14

no, that's why the example above uses s/every instead

Colin P. Hill16:02:48

Okay, figured that might be the case, it's just surprising to me

Alex Miller (Clojure team)16:02:48

coll-of assumes the coll is nth-able (which maps aren't). we may actually have a ticket to consider that, don't remember