#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
).