Clojure宏:引用,不引用和评估

时间:2015-09-09 13:56:18

标签: clojure macros quoting

我有以下代码:

(ns macroo)

(def primitives #{::byte ::short ::int})

(defn primitive? [type]
  (contains? primitives type))

(def pp clojure.pprint/pprint)

(defn foo [buffer data schema]
  (println schema))

(defmacro write-fn [buffer schema schemas]
  (let [data (gensym)]
    `(fn [~data]
       ~(cond
          (primitive? schema) `(foo ~buffer ~data ~schema)
          (vector? schema) (if (= ::some (first schema))
                             `(do (foo ~buffer (count ~data) ::short)
                                 (map #((write-fn ~buffer ~(second schema) ~schemas) %)
                                       ~data))
                             `(do ~@(for [[i s] (map-indexed vector schema)]
                                      ((write-fn buffer s schemas) `(get ~data ~i)))))
          :else [schema `(primitive? ~schema) (primitive? schema)])))) ; for debugging

(pp (clojure.walk/macroexpand-all '(write-fn 0 [::int ::int] 0)))

问题是,在评估最后一个表达式后,我得到了

=>
(fn*
 ([G__6506]
  (do
   [:macroo/int :macroo/int true false]
   [:macroo/int :macroo/int true false])))

如果有必要,我会解释代码,但是现在我只是陈述问题(这可能只是我正在制作的新手错误):

`(primitive? ~schema)

(primitive? schema)

在:else分支中分别返回true和false,因为我在cond表达式中使用第二个版本,它在它不应该的地方失败(我更喜欢第二个版本,因为它将在编译时进行评估时间,如果我没有记错的话。)

我怀疑它可能与符号命名空间的符号有关吗?

1 个答案:

答案 0 :(得分:2)

在这一行:

`(do ~@(for [[i s] (map-indexed vector schema)]
         ((write-fn buffer s schemas) `(get ~data ~i)))))

您正在当前范围内调用write-fn ,其中s只是一个符号,而不是schema中的一个条目。相反,您希望发出将在调用者范围内运行的代码:

`(do ~@(for [[i s] (map-indexed vector schema)]
         `((write-fn ~buffer ~s ~schemas) (get ~data ~i)))))

并对if的另一个分支进行类似的更改。

顺便说一下,乍一看,我觉得这并不是真的需要成为一个宏,而是可能是一个更高阶的函数:接受一个模式或其他什么,并返回一个函数数据。我的猜测是你把它作为表演的宏观,在这种情况下,我会建议你先用缓慢,简单的方法来试试。一旦你有了工作,你可以在必要时使它成为一个宏。或者,也许我错了,这里的某些东西从根本上说就是一个宏。