Определение синтаксиса макросов Clojure - PullRequest
13 голосов
/ 05 июня 2010

Я определил макрос unless следующим образом:

user=> (defmacro unless [expr body] (list 'if expr nil body))
#'user/unless
user=> (unless (= 1 2) (println "Yo"))
Yo

Как видите, он работает нормально.

Теперь в Clojure список можно определить двумя способами:

; create a list
(list 1 2 3)

; shorter notation
'(1 2 3)

Это означает, что макрос unless может быть написан без ключевого слова list. Однако это приводит к исключению Java:

user=> (unless (= 1 2) (println "Yo"))
java.lang.Exception: Unable to resolve symbol: expr in this context

Может кто-нибудь объяснить, почему это не удается?

1 Ответ

15 голосов
/ 05 июня 2010

'(foo bar baz) - это не ярлык для (list foo bar baz), а ярлык для (quote (foo bar baz)). В то время как версия списка будет возвращать список, содержащий значения переменных foo, bar и baz, версия с ' вернет список, содержащий символы foo, bar и baz. (Другими словами '(if expr nil body) совпадает с (list 'if 'expr 'nil 'body).

Это приводит к ошибке, потому что в указанной версии макрос расширяется до (if expr nil body) вместо (if (= 1 2) nil (println "Yo")) (потому что вместо замены аргументов макроса для expr и body он просто возвращает имя expr и body (которые затем рассматривается как несуществующие переменные в расширенном коде).

Ярлык, который полезен в определениях макросов, использует `. ` работает так же, как ' (т. Е. Цитирует выражение, следующее за ним), но позволяет вычислять некоторые подвыражения без кавычек, используя ~. Например, ваш макрос может быть переписан как (defmacro unless [expr body] `(if ~expr nil ~body)). Здесь важно то, что expr и body без кавычек с ~. Таким образом, расширение будет содержать их значения вместо буквальных имен expr и body.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...