Мені притупляється: я не розумію, що означає "використання для синтаксису, а не для семантики". Ось чому частина цієї відповіді стосуватиметься відповіді лунаріона , а інша частина - моєї спроби відповісти на початкове запитання.
Коли слово "семантика" використовується в програмуванні, воно позначає те, як автор мови вирішив інтерпретувати свою мову. Зазвичай це зводиться до набору формул, які перетворюють граматичні правила мови в деякі інші правила, з якими читач, як вважається, знайомий. Наприклад, Плоткін у своїй книзі « Структурний підхід до оперативної семантики» використовує логічну мову деривації, щоб пояснити, до чого оцінюється абстрактний синтаксис (що означає запускати програму). Іншими словами, якщо синтаксис є тим, що становить мову програмування на якомусь рівні, то він повинен мати певну семантику, інакше це, ну ... не мова програмування.
Але, давайте на мить забудемо формальні визначення, адже важливо зрозуміти намір. Отже, здається, що лунаріон спонукав би своїх читачів використовувати макроси, коли в програму не потрібно додавати нового значення, а лише якась абревіатура. Мені це здається дивним і на відміну від того, як насправді використовуються макроси. Нижче наведено приклади, які чітко створюють нові значення:
defun
і друзі, які є невід'ємною частиною мови, не можуть бути описані в тих же термінах, які ви описали б викликами функцій.
setf
правила, що описують, як функціонують функції, неадекватні для опису ефектів подібного виразу (setf (aref x y) z)
.
with-output-to-string
також змінює семантику коду всередині макросу. Так само with-current-buffer
і купа інших with-
макросів.
dotimes
і подібне неможливо описати з точки зору функцій.
ignore-errors
змінює семантику коду, який він обгортає.
У cl-lib
пакеті, eieio
пакеті та деяких інших майже всюдисущих бібліотеках, що використовуються в 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
Тепер ми можемо фактично обчислити, використовуючи цю мову. Тобто ми можемо прийняти синтаксичне уявлення, зрозуміти його абстрактно (іншими словами, перетворити його в абстрактний синтаксис), а потім використовувати семантичні правила для маніпулювання цим поданням.
Коли ми говоримо про сімейство мов Ліспа, основні смислові правила оцінки функції приблизно є правилами лямбда-числення:
(f x) (lambda x y) x
-----, ------------, ---
fx ^x.y x
Механізми котирування та макророзширення також відомі як інструменти метапрограмування, тобто вони дозволяють говорити про програму, а не просто програмувати. Вони досягають цього шляхом створення нової семантики за допомогою «меташару». Приклад такого простого макросу:
(a . b)
-------
(cons a b)
Це насправді не макрос у Emacs Lisp, але це міг бути. Я вибрав лише заради простоти. Зауважте, що жодне із визначених вище смислових правил не застосовуватиметься до цього випадку, оскільки найближчий кандидат (f x)
інтерпретує f
як функцію, хоча a
це не обов'язково функція.