Поиск…
Синтаксис
-
'
Символ , используемый в MACROEXPAND примера просто синтаксический сахар дляquote
оператора. Вы могли бы написать(macroexpand (quote (infix 1 + 2)))
.
замечания
Макросы - это просто функции, которые выполняются во время компиляции, то есть во время шага eval
в цикле read-eval-print .
Макросы Reader - это еще одна форма макроса, которая расширяется во время чтения, а не время компиляции.
Лучшая практика при определении макроса.
- альфа-переименование, так как макрос расширяется, связывание имен может возникнуть. Связывание конфликта не очень интуитивно понятно при использовании макроса. Поэтому, всякий раз, когда макрос добавляет привязку к области видимости, необходимо использовать
#
в конце каждого символа.
Простой инфиксный макрос
Clojure использует префиксную нотацию, то есть: оператор приходит перед своими операндами.
Например, простая сумма двух чисел:
(+ 1 2) ;; => 3
Макросы позволяют в определенной степени манипулировать языком Clojure. Например, вы можете реализовать макрос, который позволяет писать код в нотации infix (например, 1 + 2
):
(defmacro infix [first-operand operator second-operand]
"Converts an infix expression into a prefix expression"
(list operator first-operand second-operand))
Давайте сломаем то, что делает код выше:
-
defmacro
- специальная форма, которую вы используете для определения макроса. -
infix
- это имя макроса, который мы определяем. -
[first-operand operator second-operand]
- это параметры, которые этот макрос ожидает получить, когда он вызывается. -
(list operator first-operand second-operand)
- это тело нашего макроса. Он просто создаетlist
со значениями параметров, предоставляемых макросуinfix
и возвращает его.
defmacro
- особая форма, потому что она ведет себя немного по-другому по сравнению с другими конструкциями Clojure: ее параметры не сразу оцениваются (когда мы называем макрос). Это позволяет нам написать что-то вроде:
(infix 1 + 2)
;; => 3
Макрос infix
будет расширять аргументы 1 + 2
на (+ 1 2)
, что является допустимой формой Clojure, которую можно оценить.
Если вы хотите увидеть, что генерирует макрос infix
, вы можете использовать оператор macroexpand
:
(macroexpand '(infix 1 + 2))
;; => (+ 1 2)
macroexpand
, как подразумевается его именем, расширит макрос (в этом случае он будет использовать макрос infix
для преобразования 1 + 2
в (+ 1 2)
), но не позволит оценить результат макроразложения Переводчик Клоджуре.
Синтаксис цитирования и без комментариев
Пример из стандартной библиотеки ( core.clj: 807 ):
(defmacro and
"Evaluates exprs one at a time, from left to right. If a form
returns logical false (nil or false), and returns that value and
doesn't evaluate any of the other expressions, otherwise it returns
the value of the last expr. (and) returns true."
{:added "1.0"}
([] true)
([x] x)
([x & next]
`(let [and# ~x]
(if and# (and ~@next) and#))))
-
`
called syntax-quote похожа на(quote)
, но рекурсивна: она вызывает(let …)
,(if …)
и т. д., чтобы не оценивать во время макроразложения, а выводить как есть -
~
aka unquote отменяет синтаксис-цитату для отдельной формы внутри синтаксически-цитируемой формы. Таким образом, значениеx
выводится при расширении макроса (вместо вывода символаx
) -
~@
aka unquote-splicing похож на unquote, но принимает аргумент списка и расширяет его, каждый элемент списка в отдельной форме -
#
добавляет уникальный идентификатор к символам для предотвращения конфликтов имен. Он добавляет тот же идентификатор для того же символ внутри выражения синтаксических кавычек, такand#
внутриlet
иand#
внутри ,if
получат такое же имя