clojure宏 - 不知道如何创建ISeq:clojure.lang.Symbol

时间:2014-10-31 23:24:15

标签: macros clojure

我正在尝试使用clojure宏,并想知道我可能做错了什么?

我有一个简单的例子,试图根据地图动态创建函数。

例如:

(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)

宏不起作用,但调试输出看起来应该创建正确的结构:

(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__)))

任何建议都将不胜感激。感谢。

1 个答案:

答案 0 :(得分:5)

m-to-units是一个宏,这意味着每个参数都将被传递而不进行评估,这意味着在宏内部units-map的值实际上是符号units

现在,如果您直接传递地图,它将按预期工作:

 (m-to-units {:mm 1000, :m 1, :cm 100, :km 1/1000})
 ;; => #'user/to-km

 (to-cm 10)
 ;; => 1000

你能做什么 - 虽然我认为这是不好的做法 - 使用eval来获取单位地图的实际价值,无论它是作为地图还是通过符号传递:

(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