Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure macro - don't know how to create ISeq from: clojure.lang.Symbol

Tags:

macros

clojure

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.

like image 364
toolkit Avatar asked Oct 31 '14 23:10

toolkit


1 Answers

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
like image 196
xsc Avatar answered Sep 28 '22 04:09

xsc