Sebuah dekorator pada dasarnya hanya fungsi .
Contoh dalam Common Lisp:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Dalam fungsi di atas adalah simbol (yang akan dikembalikan oleh DEFUN
) dan kami menempatkan atribut pada daftar properti simbol .
Sekarang kita dapat menuliskannya di sekitar definisi fungsi:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Jika kita ingin menambahkan sintaksis mewah seperti di Python, kita menulis makro pembaca . Makro pembaca memungkinkan kita untuk memprogram pada tingkat sintaks s-ekspresi:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Kami kemudian dapat menulis:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Pembaca Lisp membaca di atas untuk:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Sekarang kami memiliki bentuk dekorator di Common Lisp.
Menggabungkan macro dan macro pembaca.
Sebenarnya saya akan melakukan terjemahan di atas dalam kode nyata menggunakan makro, bukan fungsi.
(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))))
Penggunaannya seperti di atas dengan pembaca makro yang sama. Keuntungannya adalah bahwa kompilator Lisp masih melihatnya sebagai apa yang disebut formulir tingkat atas - kompiler file * memperlakukan formulir tingkat atas secara khusus, misalnya menambahkan informasi tentang mereka ke dalam lingkungan waktu kompilasi . Pada contoh di atas kita dapat melihat bahwa makro melihat ke dalam kode sumber dan mengekstrak namanya.
Pembaca Lisp membaca contoh di atas menjadi:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Yang kemudian diperluas makro menjadi:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Makro sangat berbeda dari makro pembaca .
Makro mendapatkan kode sumber lulus, dapat melakukan apa pun yang mereka inginkan dan kemudian mengembalikan kode sumber. Sumber input tidak perlu kode Lisp yang valid. Itu bisa apa saja dan bisa ditulis sama sekali berbeda. Hasilnya harus kode Lisp yang valid. Tetapi jika kode yang dihasilkan menggunakan makro juga, maka sintaks dari kode yang tertanam dalam panggilan makro lagi bisa menjadi sintaks yang berbeda. Contoh sederhana: seseorang dapat menulis makro matematika yang akan menerima semacam sintaksis matematika:
(math y = 3 x ^ 2 - 4 x + 3)
Ekspresi y = 3 x ^ 2 - 4 x + 3
ini bukan kode Lisp yang valid, tetapi makro misalnya dapat menguraikannya dan mengembalikan kode Lisp yang valid seperti ini:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Ada banyak kasus penggunaan makro di Lisp.