Meta-programming on Lisp
Meta-programming is a powerfull technique, that allows you to manipulate your code as data, you can ask yourself “How the hell does this work exactly?”, meta-programming looks confusing but after you understand how it works your question can be change to “How to work without this?”.
Lisp is the most famous meta-programming language, it’s so natural working with this technique on Lisp.
Let’s see a simple recursive factorial algorithm, here we could use meta-programming to help us understand how this recursive function works, lisp has a trace macro that shows up step-by-step the code execution:
CL-USER> (defun factorial (x) (if (zerop x) 1 (* x (factorial (- x 1))))) FACTORIAL CL-USER> (factorial 3) 6 CL-USER> (trace factorial) ;; Tracing function FACTORIAL. (FACTORIAL) CL-USER> (factorial 6) 1. Trace: (FACTORIAL '6) 2. Trace: (FACTORIAL '5) 3. Trace: (FACTORIAL '4) 4. Trace: (FACTORIAL '3) 5. Trace: (FACTORIAL '2) 6. Trace: (FACTORIAL '1) 7. Trace: (FACTORIAL '0) 7. Trace: FACTORIAL ==> 1 6. Trace: FACTORIAL ==> 1 5. Trace: FACTORIAL ==> 2 4. Trace: FACTORIAL ==> 6 3. Trace: FACTORIAL ==> 24 2. Trace: FACTORIAL ==> 120 1. Trace: FACTORIAL ==> 720 720
The trace macro changes in execution time the factorial code to print the function steps, now we can have an idea on how to apply meta-programming in real world examples.
Now let’s implement a macro to simulate a for statement:
(defmacro for (var start stop &body body) (let ((gstop (gensym))) `(do ((,var ,start (1+ ,var)) (,gstop ,stop)) ((> ,var ,gstop)) ,@body)))
Lisp has macroexpand-1 to show the code generated for a macro, therefore:
(macroexpand-1 '(for i 10 20 (print i)))
Results:
(DO ((I 10 (1+ I)) (#:G4588 20)) ((> I #:G4588)) (PRINT I))
You can see how macros are powerful, allowing you to build sctrutctures easily, and extend the language to satisfy your need.
Wrapping up, meta-programming puts your code to work for you, think about it.