我有以下代码:
(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表达式中使用第二个版本,它在它不应该的地方失败(我更喜欢第二个版本,因为它将在编译时进行评估时间,如果我没有记错的话。)
我怀疑它可能与符号命名空间的符号有关吗?
答案 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
的另一个分支进行类似的更改。
顺便说一下,乍一看,我觉得这并不是真的需要成为一个宏,而是可能是一个更高阶的函数:接受一个模式或其他什么,并返回一个函数数据。我的猜测是你把它作为表演的宏观,在这种情况下,我会建议你先用缓慢,简单的方法来试试。一旦你有了工作,你可以在必要时使它成为一个宏。或者,也许我错了,这里的某些东西从根本上说就是一个宏。