I've been meaning to become acquainted with common lisp for a while now. Particularly its macro system and how to utilize it. As I tend to learn best while working towards a tangible goal, I wanted to come up with an idea that would use a macro and try to implement it.
I decided on a macro that would declare a function while also making the function
itself printable. The macro would call the defun
macro, as well as creating a
global variable that contains the quoted form of the function. This would allow
the function to be printable through the global variable.
After playing around in the SBCL repl, I converged on the following macro and helper function.
1
2
3
4
5
6
7
8
9
10
(defun surround-with-asterisks (sym)
(intern (concatenate 'string "*" (string sym) "*")))
(defmacro deffun (name params &body body)
(let ((var (surround-with-asterisks name)))
`(defvar ,var
'(defun ,name ,params
,@body))
`(defun ,name ,params
,@body)))
The deffun
macro has similar syntax to the defun
macro.
It takes the name of the function as its first parameter, followed by any
parameters, followed with the form body. The function name is passed
into the surround-with-asterisks
function, which aptly surrounds the symbol
parameter with some asterisk earmuffs, *symbol*
. The return value is then
bound locally to the variable var
. Using defvar
to create a global variable,
its content is assigned to the quoted form of the defun
macro, with the name,
parameters, and body variables templated in. Lastly, the function is defined as normal using defun
.
I also added a macro called prtfun
to print the quoted function stored in
the global variable:
1
2
(defmacro prtfun (fn)
`(format t "~s~%" ,(surround-with-asterisks fn)))
Now, to test it out by defining a function using the new deffun
macro:
1
2
(deffun hello (name)
(format t "name: ~a~%" name))
Calling the prtfun
macro and the hello
function:
1
2
(prtfun hello)
(hello "octobanana")
The output:
(defun hello (name) (format t "name: ~a~%" name))
name: octobanana
Using the macroexpand-1
macro to inspect what the expanded deffun
macro looks like:
1
2
3
(macroexpand-1
'(deffun hello (name)
(format t "name: ~a~%" name)))
The output:
1
(defun hello (name) (format t "name: ~a~%" name))
Using the macroexpand-1
macro to inspect what the expanded prtfun
macro looks like:
1
2
(macroexpand-1
'(prtfun hello))
The output:
1
(format t "~s~%" *hello*)
I learned a lot of little things in the process, and got some ideas that I'd like
to experiment with in the implementation of my macro interpreter
M8. What I found the most interesting, is
how regular functions defined with defun
can be called and not just
templated, but evaluated within the macro to generate code/data at the time of
macro expansion. The macro system within common lisp feels quite powerful, and
I'm already wishing similar paradigms were available natively in other languages.