Clojure宏在调用时抛出“CompilerException java.lang.IllegalStateException:Var clojure.core / unquote is unbound”

时间:2011-09-10 02:17:55

标签: macros functional-programming clojure lisp

我正在尝试编写一个clojure宏,它允许我调用一个函数并使用提供的键值从map / struct中检索参数,例如:

   (with-params  {:apple 2 :banana 3 :cherry 7} + :apple :banana)
   ;; 5

但是当我尝试使用我写的宏时:

(defmacro with-params [s f & symbols]
  `(~f ~@(map ~s ~symbols)))

致电

   (with-params  {:apple 2 :banana 3 :cherry 7} + :apple :banana)

给了我

#<CompilerException java.lang.IllegalStateException: Var clojure.core/unquote is unbound. (NO_SOURCE_FILE:0)>

有人可以帮我理解语法引用在这里的作用吗?

2 个答案:

答案 0 :(得分:4)

对于它的价值,无论如何这不应该是一个宏。一个函数非常强大,只有当map和关键字都以编译时文字形式给出时,宏版本才有效。

(defmacro with-params-macro [s f & symbols]
  `(~f ~@(map s symbols)))

(defn with-params-fn [s f & symbols]
  (apply f (map s symbols)))

user> (with-params-macro {:x 1} (fn [z] z) :x)
1
user> (let [params {:x 1}] 
        (with-params-macro params (fn [z] z) :x))
nil

user> (let [params {:x 1}] 
        (with-params-fn params (fn [z] z) :x))
1

答案 1 :(得分:3)

`(~f ~@(map ~s ~symbols))不起作用的原因是编译器在unquote~)内部对不必要的unquote-splicing~@)进行了扼流。 unquote-splicing取消引用外部syntax-quote`),因此内部两个unquote没有任何匹配的syntax-quote,这就是为什么你得到了“未绑定”错误。

您要做的是首先评估(map s symbols)以获取操作数序列,然后将展平结果传递给函数(~f);因此正确的版本是:

(defmacro with-params [s f & symbols] `(~f ~@(map s symbols)))

您可以使用以下方法轻松验证:

(macroexpand '(with-params {:a 1 :b 2 :c 5} * :a :b :c))    ;; (* 1 2 5)
(with-params {:a 1 :b 2 :c 5} * :a :b :c)                   ;; 10