我有以下Clojure宏:
(defmacro with-model
[ref & body]
`(tx
(let [ds# (when (vector? (first ~body)) (ffirst ~body))
name# (when (vector? (first ~body)) (second (first ~body)))
~ref (model ds# name#)]
(do ~@body))))
我试图像这样使用它:
(deftest with-model-test
(with-model sandwich
(let [nodes (-> sandwich .listObjects iterator-seq)]
(is nodes))))
或者这个:
(deftest with-model-test
(with-model sandwich [*ds* "named-model"]
(let [nodes (-> sandwich .listObjects iterator-seq)]
(is nodes))))
这个想法是sandwich
现在应该引用Model
,但我得到一个运行时异常:
Unable to resolve symbol: sandwich in this context
如果我在宏中(println ~ref)
,我会得到模型实例。如果我(println '~ref)
,我会sandwich
。我应该怎么做呢?
答案 0 :(得分:1)
将with-model
宏用作(with-model sandwich (let [node (-> sandwich)]))
时的宏展开如下所示(删除了名称空间,缩短了绑定名称并进行了一些格式化):
(macroexpand-1 '(with-model sandwich (let [node (-> sandwich)])))
(tx
(let [ds (when (vector? (first ((let [node (-> sandwich)]))))
(ffirst ((let [node (-> sandwich)]))))
name (when (vector? (first ((let [node (-> sandwich)]))))
(second (first ((let [node (-> sandwich)])))))
sandwich (model ds name)]
(let [node (-> sandwich)])))
正如您所看到的,sandwich
在let
被定义之前被使用,因为宏生成的代码可以解释扩展后的第二个参数。解决这个问题的一种方法是让宏在扩展之前解决问题。一般来说,我尝试这样做是为了进行更简单的扩展,即使它有时意味着更复杂的宏代码,而不是在这种情况下很难。
(defmacro with-model
[ref & [x & _ :as body]]
`(tx
(let [ds# ~(when (vector? x) (first x))
name# ~(when (vector? x) (second x))
~ref (model ds# name#)]
~@body)))