clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
Eamonn Sullivan 2021-03-18T15:35:05.002200Z

Here's a question that'll probably out me as someone who doesn't understand the concept: Is is possible to spec a "closed" map, one that has some number of required and optional keys, but no other keys?

Eamonn Sullivan 2021-03-18T15:36:23.002300Z

I'm trying to translate the JSON schema concept of

additionalProperties = false

Eamonn Sullivan 2021-03-18T15:39:25.002500Z

probably s/map-of #{...} will try that.

Eamonn Sullivan 2021-03-18T15:44:50.002700Z

nope

Eamonn Sullivan 2021-03-18T15:44:57.002900Z

I guess that's a spec2 thing?

borkdude 2021-03-18T15:45:44.003100Z

This will be supported in spec2, malli already supports it

borkdude 2021-03-18T15:46:29.003400Z

you could also try to do this using a predicate, (s/and ... (fn [map] check-the-keys))

Eamonn Sullivan 2021-03-18T15:46:57.003700Z

šŸ’” Thank you!

alexmiller 2021-03-18T15:55:26.003900Z

the important part of our perspective in spec 2 is that we think that "closed" should be a property of the check, not a property of the spec

borkdude 2021-03-18T15:56:36.004100Z

good point!

borkdude 2021-03-18T15:57:39.004300Z

@alexmiller Are you considering doing some journal updates again in the future? I for one really enjoyed them.

Eamonn Sullivan 2021-03-18T15:58:11.004500Z

I think I see. So, I have an API that's dumb as a bag of rocks and will spit out any blob of json that includes anything other than, say, five keys. There are many combinations of those that are allowed, but it will throw a hissy fit if a key isn't one of those five.

Eamonn Sullivan 2021-03-18T16:00:09.004700Z

Only one of them is required and the rest optional, which all fit nicely into (s/keys :req-un [,,,] :opt-un [,,,]) until I did a typo in one of the opt-uns.

Eamonn Sullivan 2021-03-18T16:01:20.005Z

passes the spec; fails in life.

vemv 2021-03-18T16:29:05.005200Z

> should be a property of theĀ check, not a property of theĀ spec (FWIW!) while one can see the value of this, it's also hard to see how this would not conflict with DRY. If one tried to DRY out n identical calls as a reusable "thing", what would be that thing be? It seems to me that inherently, the 'thing' would effectively become a spec (whether it's an actual spec, or a de-facto spec: e.g. a defn)

alexmiller 2021-03-18T16:31:04.005400Z

makes sense to me

Eamonn Sullivan 2021-03-18T17:00:31.005600Z

Actually, this was a bit too easy. Must have missed something?

(s/def ::environments #{:int :test :stage :live})
(s/def ::valid-top-level-keys #{:name :description :values})
(s/def ::name string?)
(s/def ::description string?)
(s/def ::values (s/map-of ::environments string?))

(s/def ::config (s/and
                 (s/keys :req-un [::name]
                         :opt-un [::values ::description])
                 (s/map-of ::valid-top-level-keys any?)))

(s/def ::release-config (s/coll-of ::config))

(comment
  (s/valid? ::release-config [{:name "something" :description "some description"}])
  (s/valid? ::release-config [{:name "something" :desciption "some description with typo"}])
  )

Eamonn Sullivan 2021-03-18T17:00:49.005800Z

I was already doing something similar with the :environments spec.