4
votes

I'm using Clojure to implement a (written) standards document. In general I'm pleased with the way Clojure allows me to write code that lines up with the different parts of the standard. With an eye on the future I am experimenting with writing a clojure.spec for it. In the document they define various structured data elements with named fields. However fields in different structures have the same name, for example the 'red' structure has a 'value' field which is a string, but the 'blue' structure has a 'value' field which is an integer. How can I handle this when it comes to writing specs?

(s/def ::value ???)
(s/def ::red (s/keys :req [::value ...]))
(s/def ::blue (s/keys :req [::value ...]))

The official advice, as I understand it, is that named keys should have the same semantics everywhere.

How should I approach this? I could call them 'red-value' and 'blue-value' but this makes the correspondence between the code and the standard less clear. Could I put every structure in its own namespace?

1

1 Answers

5
votes

Your example is using the current namespace for all of your spec names, but you should leverage namespaces to disambiguate names.

(s/def ::red (s/keys :req [:red/value ...]))
(s/def ::blue (s/keys :req [:blue/value ...]))

You can use these specs with maps like:

(s/valid? ::red {:red/value "foo"})
(s/valid? ::blue {:blue/value 100})

Additionally, s/keys supports :req-un option to link named specs to unqualified attribute names, if that's what you have to work with.

(s/def ::red (s/keys :req-un [:red/value ...]))
(s/def ::blue (s/keys :req-un [:blue/value ...]))

You could validate with values like:

(s/valid? ::red {:value "foo"})
(s/valid? ::blue {:value 100})