Макросы необходимы для обеспечения доступа к языковым функциям. Например, в TXR Lisp у меня есть одна функция с именем sys:capture-cont
для захвата продолжения с разделителями. Но это неудобно использовать само по себе. Таким образом, вокруг него обернуты макросы, такие как suspend
или obtain
и yield
, которые предоставляют альтернативные модели для возобновляемого, приостановленного выполнения. Они реализованы здесь .
Другим примером является сложный макрос defstruct
, который предоставляет синтаксис для определения типа структуры. Он компилирует свои аргументы в lambda
-s и другой материал, который передается в функцию make-struct-type
. Если бы программы использовали make-struct-type
непосредственно для определения ООП-структур, они были бы ужасны:
1> (macroexpand '(defstruct foo bar x y (z 9) (:init (self) (setf self.x 42))))
(sys:make-struct-type 'foo 'bar '()
'(x y z) ()
(lambda (#:g0101)
(let ((#:g0102 (struct-type #:g0101)))
(unless (static-slot-p #:g0102 'z)
(slotset #:g0101 'z
9)))
(let ((self #:g0101))
(setf (qref self x)
42)))
())
Хлоп! Многое должно быть правильно. Например, мы не просто вставляем 9
в слот z
, потому что (из-за наследования) мы можем фактически быть базовой структурой производной структуры, а в производной структуре z
может быть статическим слотом (делится на экземпляры). Мы бы забили значение, установленное для z
в производном классе.
В ANSI Common Lisp хорошим примером макроса является loop
, который обеспечивает полный язык для параллельной итерации. Один loop
вызов может выражать целый сложный алгоритм.
Макросы позволяют нам независимо думать о синтаксисе, который нам нужен в языковой функции, и об основных функциях или специальных операторах, необходимых для его реализации. Независимо от того, какой выбор мы сделаем в этих двух, макросы соединят их для нас. Мне не нужно беспокоиться, что make-struct
уродливо использовать, поэтому я могу сосредоточиться на технических аспектах; Я знаю, что макрос может выглядеть одинаково независимо от того, как я делаю различные компромиссы. Я принял решение о проектировании, что вся инициализация структуры будет выполняться некоторыми функциями, зарегистрированными для типа. Хорошо, это означает, что мой макрос должен взять все инициализации в синтаксисе определения слота и скомпилировать анонимные функции, где инициализация слота выполняется кодом, сгенерированным в теле.
Макросы - это компиляторы для битов синтаксиса, для которых функции и специальные операторы являются целевым языком.
Иногда люди (обычно не являющиеся пользователями Lisp) критикуют макросы следующим образом: макросы не добавляют никаких возможностей, только синтаксический сахар.
Во-первых, синтаксический сахар - это возможность.
Во-вторых, вы также должны рассматривать макросы с «общей точки зрения хакера»: комбинировать макросы с работой на уровне реализации. Если я добавляю функции к диалекту Lisp, такие как структуры или продолжения, я фактически расширяю возможности. Участие макросов в этом предприятии необходимо . Хотя макросы не являются источником новой силы (он не исходит от самих макросов), они помогают приручить и использовать его, придав ему выражение.
Если у вас нет sys:capture-cont
, вы не можете просто взломать его поведение с помощью макроса suspend
. Но если у вас нет макросов, вы должны сделать что-то ужасно неудобное, чтобы предоставить доступ к новой функции, которая не является библиотечной функцией, а именно жестко запрограммировать некоторые новые правила структуры фраз в парсер .