Clojure宏:从映射创建本地变量

Clojure宏:从映射创建本地变量,clojure,Clojure,我有这个示例代码,在这里我通过迭代映射的键值对来创建变量 (defmacro block [bindings & body] `(let [ ~@(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `~v]) bindings) ] ~body)) (block {:a 1 :b 2} (if true (prn "true" a b) (prn "false"))) 它很好用。 输出:“正确”1 2

我有这个示例代码,在这里我通过迭代映射的键值对来创建变量

(defmacro block [bindings & body] 
  `(let [
    ~@(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `~v]) bindings) ]
  ~body))

(block {:a 1 :b 2} (if true (prn "true" a b) (prn "false")))
它很好用。 输出:“正确”1 2


但是现在我想传递与var相同的映射,但是它认为是一个异常

IllegalArgumentException不知道如何从以下位置创建ISeq:clojure.lang.Symbol

(def ctx {:a 3 :b 4})

(block ctx (if true (prn "true" a b) (prn "false")))

当您传递一个引用保存映射的变量的符号时,它不起作用的原因是宏只看到符号文字,而不是它引用的值。当您向其传递映射文字时,宏将看到映射文字

如果希望它也支持符号,则需要
解析
符号引用的变量并获取其值,例如
(var get(解析绑定))

您还需要使用
~@
正文
拼接到表单中,因为
正文
将是一个表单序列,即使它只包含一个表单

在对宏进行故障排除时,它还可以帮助查看宏是如何展开的:

(macroexpand-1 '(block ctx (if true (prn "true" a b) (prn "false"))))
=> (clojure.core/let [a 3 b 4] (if true (prn "true" a b) (prn "false")))

当您传递一个引用保存映射的变量的符号时,它不起作用的原因是宏只看到符号文字,而不是它引用的值。当您向其传递映射文字时,宏将看到映射文字

如果希望它也支持符号,则需要
解析
符号引用的变量并获取其值,例如
(var get(解析绑定))

您还需要使用
~@
正文
拼接到表单中,因为
正文
将是一个表单序列,即使它只包含一个表单

在对宏进行故障排除时,它还可以帮助查看宏是如何展开的:

(macroexpand-1 '(block ctx (if true (prn "true" a b) (prn "false"))))
=> (clojure.core/let [a 3 b 4] (if true (prn "true" a b) (prn "false")))

当您想在一个应用程序中使用它时会发生什么func@JAtkin宏在函数中的行为与在其他任何地方的行为相同-它们被“扩展”在对调用它们的代码求值之前。当我传递变量时,这会为我抛出一个NullPointerException。如果要在func@JAtkin宏在函数中的行为与在其他任何地方的行为相同-它们被“扩展”在计算调用它们的代码之前。当我传递变量时,这会为我抛出一个NullPointerException。