Um decorador é basicamente apenas uma função .
Exemplo no Lisp comum:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Acima, a função é um símbolo (que seria retornado por DEFUN
) e colocamos os atributos na lista de propriedades do símbolo .
Agora podemos escrever em torno de uma definição de função:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Se queremos adicionar uma sintaxe sofisticada como no Python, escrevemos uma macro de leitor . Uma macro de leitor nos permite programar no nível da sintaxe da expressão s:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Podemos então escrever:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
O leitor Lisp lê acima para:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Agora, temos uma forma de decoradores no Common Lisp.
Combinando macros e macros de leitor.
Na verdade, eu faria a tradução acima em código real usando uma macro, não uma função.
(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))))
O uso é como acima com a mesma macro do leitor. A vantagem é que o compilador Lisp ainda o vê como um formulário de nível superior - o compilador de arquivos * trata os formulários de nível superior especialmente, por exemplo, adiciona informações sobre eles no tempo de compilação ambiente de . No exemplo acima, podemos ver que a macro analisa o código-fonte e extrai o nome.
O leitor Lisp lê o exemplo acima em:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Que então é expandida para a macro:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Macros são muito diferentes das macros do leitor .
As macros passam o código fonte, podem fazer o que quiserem e depois retornar o código fonte. A fonte de entrada não precisa ser um código Lisp válido. Pode ser qualquer coisa e pode ser escrito totalmente diferente. O resultado deve ser um código Lisp válido. Mas se o código gerado também estiver usando uma macro, a sintaxe do código incorporado na chamada de macro poderá ser novamente uma sintaxe diferente. Um exemplo simples: pode-se escrever uma macro matemática que aceite algum tipo de sintaxe matemática:
(math y = 3 x ^ 2 - 4 x + 3)
A expressão y = 3 x ^ 2 - 4 x + 3
não é um código Lisp válido, mas a macro poderia, por exemplo, analisá-la e retornar um código Lisp válido assim:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Existem muitos outros casos de uso de macros no Lisp.