我有这个示例代码,通过迭代映射的键值对来创建var。
/var/script/config.sh | at now + 1 minute
工作正常。 Ouput:“真” 1 2
但是现在我想传递与var相同的映射,但是它引发了异常。
IllegalArgumentException不知道如何从clojure.lang.Symbol创建ISeq
(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")))
答案 0 :(得分:5)
当您传递指向包含地图的var的符号时,它不起作用的原因是因为宏仅看到符号文字,而不是其所指向的值。当您将其传递给地图文字时,宏会看到地图文字。
如果您希望它也支持符号,则需要resolve
符号所引用的var并获取其值,例如(var-get (resolve bindings))
:
(defmacro block [bindings & body]
`(let [~@(mapcat (fn [[k v]] [(if (symbol? k)
k
(symbol (name k))) `~v])
(cond
(map? bindings) bindings
(symbol? bindings) (var-get (resolve bindings))
:else (throw (Exception. "bindings must be map or symbol"))))]
~@body))
(block ctx (if true (prn "true" a b) (prn "false")))
"true" 3 4
(block {:a 3 :b 4} (if true (prn "true" a b) (prn "false")))
"true" 3 4
您还需要使用body
将~@
“拼接”到表单中,因为body
将是一系列表单,即使它仅包含一个表单。
它还可以帮助您查看对宏进行故障排除时的扩展方式:
(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")))