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



我想写一个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)>

有人能帮我理解这里的语法引用是如何工作的吗?

无论如何,这都不应该是一个宏。函数是非常强大的,宏版本只有在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

`(~f ~@(map ~s ~symbols))不起作用的原因是编译器阻塞了unquote-splicing (~@)内部不必要的unquote (~)。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

最新更新