Reputation: 676
Is there any trick to make this work without passing the data directly or using eval?
(defmacro pair-defs [data]
(cons 'do
(for [[k v] data]
`(def ~k ~v))))
(def data '((a 1) (b 2) (c 3)))
(pair-defs data)
Upvotes: 1
Views: 421
Reputation: 17859
If your data
var is defined in namespace before the macro call,you can use some namespace manipulating functions to resolve it's value by name:
(defmacro pair-defs [data]
`(do ~@(for [[k v] @(ns-resolve *ns* data)]
`(def ~k ~v))))
user> (def data [['a 10] ['b 20]])
#'user/data
user> (pair-defs data)
#'user/b
or to handle both literal data and data by var name:
(defmacro pair-defs [data]
(let [dt (if (symbol? data) @(ns-resolve *ns* data) data)]
`(do ~@(for [[k v] dt]
`(def ~k ~v)))))
the call is expanded to the desired form: (do (def a 10) (def b 20))
So resolving the namespace val without dirty tricks like eval
is totally possible, but i find it to be quite an unneeded usage of macros. For example your task is easily replaceable by plain function call.
(defn pair-defs1 [data]
(doseq [[k v] data]
(intern *ns* k v)))
it would work on any data
sequence, be it local or global, literal or generated
Upvotes: 3