I'm experimenting with clojure macros, and wondered what I might be doing wrong?
I have a simple example, of attempting to dynamically create functions based on a map.
For example:
(def units {:cm 100
:mm 1000
:m 1
:km 1/1000})
(defn m-to-unit-helper [[k v]]
(let [f (symbol (str "to-" (name k)))]
`(defn ~f [m#] (* ~v m#))))
(defmacro m-to-units [units-map]
(let [funcs (map m-to-unit-helper units-map)]
`(do ~@funcs)))
; complains with: Don't know how to create ISeq from: clojure.lang.Symbol
(m-to-units units)
; To try and debug
(defn debug [units-map]
(let [funcs (map m-to-unit-helper units-map)]
(clojure.pprint/pprint `(do ~@funcs))))
; see below
(debug units)
The macro doesn't work, but the debug output look like it should create the correct structure:
(do
(clojure.core/defn
to-mm
[m__32709__auto__]
(clojure.core/* 1000 m__32709__auto__))
(clojure.core/defn
to-m
[m__32709__auto__]
(clojure.core/* 1 m__32709__auto__))
(clojure.core/defn
to-cm
[m__32709__auto__]
(clojure.core/* 100 m__32709__auto__))
(clojure.core/defn
to-km
[m__32709__auto__]
(clojure.core/* 1/1000 m__32709__auto__)))
Any advice would be greatly appreciated. Thanks.
m-to-units
is a macro which means that every parameter will be passed without being evaluated, meaning that inside the macro the value of units-map
is actually the symbol units
.
Now, if you pass the the map directly, it'll work as intended:
(m-to-units {:mm 1000, :m 1, :cm 100, :km 1/1000})
;; => #'user/to-km
(to-cm 10)
;; => 1000
What you could do - although I'd consider it bad practice - is using eval
to get the actual value of the units map, no matter whether it's passed as a map or via a symbol:
(defmacro m-to-units
[units-map]
(let [funcs (map m-to-unit-helper (eval units-map))]
`(do ~@funcs)))
(m-to-units units)
;; => #'user/to-km
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With