Почему не выполняется эта функция clojure?

#macros #clojure

#макросы #clojure

Вопрос:

Я работаю над некоторыми примерами clojure из braveclojure:

http://www.braveclojure.com/writing-macros/

В настоящее время я пытаюсь выполнить это

 (ns turtle (:use clojure.pprint))

(def criticisms {:good "good code:" :bad "bad code:"})

(defn criticize-code
    [[critkey code]]
    `(println (~critkey criticisms) (quote ~code)))

(defmacro code-critic
    [code-evaluations]
    `(do ~@(map criticize-code code-evaluations)))

(println "executed code critic")
(code-critic {:good (  1 1) :bad (1   1)})
(println "code critic expansion")
(pprint (macroexpand '(code-critic {:good (  1 1) :bad (1   1)})))

;why isn't this executing?
(println "criticize code expansion")
(criticize-code [:good '(  1 1)])
 

В принципе, я могу убедиться, что criticize-code возвращает правильно отформатированный код println ; но я не могу его выполнить … может кто-нибудь, пожалуйста, сказать мне, что я делаю не так?

Спасибо!

Ответ №1:

Вызывается функция criticize-code . Квазикавычка ` в теле функции является макросом чтения для синтаксической кавычки, что означает, что следующая println форма после ее прохождения через средство чтения синтаксических кавычек будет возвращена как структура данных, а не выполнена. criticize-code Функция семантически эквивалентна

 (defn criticize-code
  [[critkey code]]
  (list 
    'clojure.core/println 
    (list critkey 'turtle/criticisms) 
    (list 'quote code)))
 

Если вы хотите обработать результирующую структуру данных как код в REPL, вы можете eval сделать это напрямую.

 turtle=> (criticize-code [:good '(  1 1)])
(clojure.core/println (:good turtle/criticisms) (quote (  1 1)))
turtle=> (eval (criticize-code [:good '(  1 1)]))
good code: (  1 1)
nil
 

Итак, зачем вам нужна функция, которая работает подобным образом? В качестве вспомогательного средства для макроса, как здесь для code-critic . Макросы обрабатывают код как данные как код. Поэтому, если вы вставляете вспомогательную функцию на as-data этапе, ей нужно будет вернуть свой результат в виде данных. В противном случае код, который вы хотите скомпилировать, просто выполняется во время «компиляции», а вместо него компилируется возвращаемое значение ( println returns nil ).