Декоратор в основному тільки функція .
Приклад у Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Вгорі функцією є символ (який би повертався DEFUN
), і ми ставимо атрибути на символі список властивостей .
Тепер ми можемо записати це навколо визначення функції:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Якщо ми хочемо додати фантазійний синтаксис, як у Python, ми пишемо макрос читача . Макрос читача дозволяє нам програмувати на рівень синтаксису s-виразу:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Тоді ми можемо написати:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Читач Lisp читає вище:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Тепер у нас є форма декораторів у Common Lisp.
Поєднання макросів та макросів читання.
Насправді я б зробив вище переклад у реальний код, використовуючи макрос, а не функцію.
(defmacro defdecorator (decorator arg form)
`(progn
,form
(,decorator ,arg ',(second form))))
(set-macro-character
#\@
(lambda (stream char)
(declare (ignore char))
(let* ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(defdecorator ,decorator ,arg ,form))))
Використання, як описано вище, з тим же макросом читача. Перевага полягає в тому, що компілятор Lisp все ще сприймає його як так звану форму верхнього рівня - компілятор файлів * обробляє форми вищого рівня спеціально, наприклад, він додає інформацію про них у середовище часу компіляції. . У наведеному вище прикладі ми бачимо, що макрос заглядає у вихідний код і витягує ім'я.
Читач Lisp читає наведений вище приклад на:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Потім розширюється макрос на:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Макроси дуже відрізняються від макросів для читання .
Макроси передають вихідний код, можуть робити все, що завгодно, а потім повертати вихідний код. Вхідним джерелом не повинен бути дійсний код Lisp. Це може бути що завгодно, і це можна було б написати зовсім іншим. У результаті повинен бути дійсний код Lisp. Але якщо згенерований код також використовує макрос, то синтаксис коду, вбудованого в виклик макросу, може знову бути іншим синтаксисом. Простий приклад: можна написати математичний макрос, який би прийняв якийсь математичний синтаксис:
(math y = 3 x ^ 2 - 4 x + 3)
Вираз y = 3 x ^ 2 - 4 x + 3
не є дійсним кодом Lisp, але макрос може, наприклад, проаналізувати його та повернути дійсний код Lisp таким чином:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
У Ліспі багато інших випадків використання макросів.