Макрос Clojure выдает «CompilerException java.lang.IllegalStateException: Var clojure.core / unquote не связан» при вызове - PullRequest
4 голосов
/ 10 сентября 2011

Я пытаюсь написать макрос clojure, который позволяет мне вызывать функцию и извлекать аргументы из карты / структуры, используя указанные ключевые значения, такие как:

   (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 ]

4 голосов
/ 12 сентября 2011

Для чего бы это ни стоило, это не должно быть макросом в любом случае.Функция очень мощная, и версия макроса будет работать, только если карта и ключевые слова заданы как литералы времени компиляции.

(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
3 голосов
/ 10 сентября 2011

Причина, по которой `(~f ~@(map ~s ~symbols)) не работает, заключается в том, что компилятор перекрывает ненужные unquote (~) внутри unquote-splicing (~@). unquote-splicing заключает в кавычки внешний syntax-quote (`), поэтому внутренние два unquote не имеют совпадений syntax-quote, поэтому вы получили ошибку «unbound».

То, что вы хотите сделать, - это сначала (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
...