Reputation: 4582
Say that I have the following array of keywords
(def keys [::description ::url ::mailing-list])
which I want to reuse in two specs; one for defining a map and one for defining optional arguments to a function.
(require '[clojure.spec :as spec])
(spec/def ::project-map
(spec/keys :opt-un keys))
(spec/def ::project-args
(spec/keys* :opt-un keys))
The problem is then that the keys
and keys*
get passed the quoted symbol 'keys
and not the resolved value held in the variable it's referring to.
So my question is: Is it possible to resolve the value of keys at read time like the common lisp #.
reader macro, or does the macro have to be redefined to resolve the symbol if it gets a symbol instead of a list literal?
Upvotes: 4
Views: 539
Reputation: 12747
I had a similar issue where I was trying to register a spec dynamically. I had trouble because I wasn't passing in literal keywords, so the solution was to eval the macro's args and then refer to those:
(def my-key ::this-is-a-test-key)
(defmacro define-spec [spec-key validator]
(let [local-key (eval spec-key)]
`(s/def ~local-key ~validator)))
(define-spec my-key int?)
;;testing it
(s/valid? my-key 123)
(s/valid? my-key "ASDASD")
It's also worth noting that in cljs, calling define-spec
will return nils because cljs doesn't support macros.
Upvotes: 0
Reputation: 1
Or you could just do:
(spec/def ::project-map (spec/keys ::opt-un ~keys))
Might need to use qualitified namespaces (e.g. ::opt-un
)
spec/def
is already a macro ;)
Upvotes: -1
Reputation: 2968
You could also wrap them in another macro, since macros are responsible for how their arguments are evaluated.
(defmacro def-both [name name* keys]
`(do (s/def ~name (s/keys :opt-un ~keys))
(s/def ~name* (s/keys* :opt-un ~keys))))
(user/def-both ::project-map ::project-args [::description ::url ::mailing-list])
Upvotes: 1
Reputation: 4582
You can construct a half-icky solution with eval
:
(spec/def ::project-map
(eval `(spec/keys :opt-un ~keys)))
(spec/def ::project-args
(eval `(spec/keys* :opt-un ~keys)))
This was suggested by mpened on the clojurians slack.
Upvotes: 1