Dekorator jest w zasadzie tylko funkcja .
Przykład w Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Powyżej funkcja jest symbolem (który zostałby zwrócony przez DEFUN
) i umieszczamy atrybuty na liście właściwości symbolu .
Teraz możemy napisać to wokół definicji funkcji:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Jeśli chcemy dodać fantazyjną składnię, jak w Pythonie, piszemy makro czytnika . Makro czytnika pozwala nam programować na poziomie składni s-expression:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Następnie możemy napisać:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Czytnik Lisp czyta powyżej, aby:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Teraz mamy formę dekoratorów w Common Lisp.
Łączenie makr i makr czytelników.
Właściwie zrobiłbym powyższe tłumaczenie w prawdziwym kodzie przy użyciu makra, a nie funkcji.
(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))))
Zastosowanie jest takie samo jak powyżej w przypadku tego samego makra czytnika. Zaletą jest to, że kompilator Lisp nadal postrzega go jako tak zwaną formę najwyższego poziomu - kompilator plików * traktuje formularze najwyższego poziomu specjalnie, na przykład dodaje informacje o nich do środowiska czasu kompilacji . W powyższym przykładzie widzimy, że makro przegląda kod źródłowy i wyodrębnia nazwę.
Czytnik Lisp wczytuje powyższy przykład na:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Który następnie zostaje makro rozszerzony do:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Makra bardzo różnią się od makr czytników .
Makra otrzymują kod źródłowy, mogą robić, co chcą, a następnie zwracają kod źródłowy. Źródło wejściowe nie musi być poprawnym kodem Lisp. Może to być wszystko i może być napisane zupełnie inaczej. Wynik musi wtedy być poprawnym kodem Lisp. Ale jeśli wygenerowany kod również korzysta z makra, wówczas składnia kodu osadzonego w wywołaniu makra może znów mieć inną składnię. Prosty przykład: można napisać makro matematyczne, które akceptuje pewną składnię matematyczną:
(math y = 3 x ^ 2 - 4 x + 3)
Wyrażenie y = 3 x ^ 2 - 4 x + 3
nie jest poprawnym kodem Lisp, ale makro może na przykład parsować je i zwrócić prawidłowy kod Lisp w następujący sposób:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Istnieje wiele innych przypadków użycia makr w Lisp.