Я буду тупым: я не понимаю, что означает «использовать для синтаксиса, а не для семантики». Вот почему часть этого ответа будет касаться ответа lunaryon , а другая часть будет моей попыткой ответить на первоначальный вопрос.
Когда слово «семантика» используется в программировании, оно относится к тому, как автор языка решил интерпретировать свой язык. Обычно это сводится к набору формул, которые превращают грамматические правила языка в некоторые другие правила, с которыми, как предполагается, читатель знаком. Например, Плоткин в своей книге « Структурный подход к операционной семантике» использует язык логического вывода для объяснения того, во что оценивается абстрактный синтаксис (что означает запуск программы). Другими словами, если синтаксис является тем, что составляет язык программирования на некотором уровне, то он должен иметь некоторую семантику, в противном случае это, ну ... не язык программирования.
Но давайте на минутку забудем формальные определения, ведь важно понять намерение. Таким образом, кажется, что lunaryon будет поощрять своих читателей использовать макросы, когда в программу не нужно добавлять никакого нового значения, а просто какое-то сокращение. Для меня это звучит странно и резко контрастирует с тем, как на самом деле используются макросы. Ниже приведены примеры, которые явно создают новые значения:
defun
и друзья, которые являются неотъемлемой частью языка, не могут быть описаны в тех же терминах, которые вы описали бы вызовами функций.
setf
правила, описывающие, как работают функции, не подходят для описания эффектов выражения вроде (setf (aref x y) z)
.
with-output-to-string
также изменяет семантику кода внутри макроса. Аналогично with-current-buffer
и куча других with-
макросов.
dotimes
и подобное не может быть описано в терминах функций.
ignore-errors
изменяет семантику кода, который он переносит.
Существует целая куча макросов, определенных в cl-lib
package, eieio
package и некоторых других почти повсеместно распространенных библиотеках, используемых в Emacs Lisp, которые, хотя и могут выглядеть как функциональные формы, имеют разные интерпретации.
Так что, во всяком случае, макросы - это инструмент для введения новой семантики в язык. Я не могу придумать альтернативный способ сделать это (по крайней мере, в Emacs Lisp).
Когда я не буду использовать макросы:
Когда они не вводят никаких новых конструкций, с новой семантикой (то есть функция будет делать то же самое хорошо).
Когда это просто своего рода удобство (то есть создание макроса с именем, mvb
который расширяется, cl-multiple-value-bind
чтобы сделать его короче).
Когда я ожидаю, что макрос для обработки ошибок будет скрыт макросом (как было отмечено, макросы трудно отлаживать).
Когда я бы предпочел макросы над функциями:
Когда доменные понятия скрыты вызовами функций. Например, если нужно передать один и тот же context
аргумент в кучу функций, которые нужно вызывать последовательно, я бы предпочел заключить их в макрос, где context
аргумент скрыт.
Когда универсальный код в форме функции слишком многословен (голые итерационные конструкции в Emacs Lisp слишком многословны на мой вкус и излишне подвергают программиста деталям реализации низкого уровня).
иллюстрация
Ниже приведена разбавленная версия, предназначенная для того, чтобы дать интуитивное представление о том, что подразумевается под семантикой в информатике.
Предположим, у вас очень простой язык программирования с такими синтаксическими правилами:
variable := 1 | 0
operation := +
expression := variable | (expression operation expression)
с помощью этого языка мы можем создавать «программы», такие как (1+0)+1
или 0
или ((0+0))
и т. д.
Но пока мы не предоставили семантические правила, эти «программы» не имеют смысла.
Предположим, мы теперь оснастили наш язык (семантическими) правилами:
0+0 0+1 1+0 1+1 (exp)
---, ---, ---, ---, -----
0 1+0 1 0 exp
Теперь мы можем фактически вычислить, используя этот язык. То есть мы можем взять синтаксическое представление, понять его абстрактно (другими словами, преобразовать его в абстрактный синтаксис), а затем использовать семантические правила для управления этим представлением.
Когда мы говорим о семействе языков Lisp, основные семантические правила оценки функций примерно такие же, как в лямбда-исчислении:
(f x) (lambda x y) x
-----, ------------, ---
fx ^x.y x
Механизмы цитирования и макроразложения также известны как инструменты метапрограммирования, то есть они позволяют говорить о программе, а не просто о программировании. Они достигают этого, создавая новую семантику, используя «мета-слой». Пример такого простого макроса:
(a . b)
-------
(cons a b)
На самом деле это не макрос в Emacs Lisp, но это могло бы быть. Я выбрал только ради простоты. Обратите внимание, что ни одно из семантических правил, определенных выше, не будет применяться к этому случаю, поскольку ближайший кандидат (f x)
интерпретирует f
как функцию, хотя a
не обязательно является функцией.