Programming A Fuzzy Time Function

Ha Ling Peak In The Clouds

Macros in Common Lisp

updated: Thu 7 Feb 2019


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
(defun surround-with-asterisks (sym)
  (intern (concatenate 'string "*" (string sym) "*")))

(defmacro deffun (name params &body body)
  (let ((var (surround-with-asterisks name)))
    `(progn (defvar ,var
      '(defun ,name ,params
        ,@body))
    (eval ,var))))

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 quoted function stored in var from earlier is passed to eval to define the function.

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:

(progn
  (defvar *hello* '(defun hello (name) (format t "name: ~a~%" name)))
  (eval *hello*))

Using the macroexpand-1 macro to inspect what the expanded prtfun macro looks like:

1 2
(macroexpand-1
  '(prtfun hello))

The output:

(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.

Back to Top

Programming A Fuzzy Time Function

Ha Ling Peak In The Clouds