Calling macroexpand at runtime

Viewed 349

Is it possible to expand a macro at run time in a compiled lisp executable? I would expect it to be impossible as macro expansion can only happen pre-compilation yet when i call macroexpand in compiled code i get output.

2 Answers

A macro is a function that's normally called automatically during compilation or evaluation, and whose return value is then compiled or evaluated in place of the original expression.

But since it's just a function, there's nothing preventing it from being called explicitly during run time as well, and that's what MACROEXPAND and MACROEXPAND-1 do.

It's roughly equivalent to:

(defun macroexpand-1 (form &optional env)
  (if (and (listp form) (car form)) ;; list expression
      (let ((macfun (macro-function (car form)))
        (if macfun
            (funcall macfun form env)
            form))
      form))

(Note that this definition doesn't handle symbol macros or use *MACROEXPAND-HOOK*, to keep it simple.)

It's possible to use EVAL-WHEN when defining the macro to make the macro definition only available in the compilation environment. If you do that, trying to expand at run time will fail.

In Lisp, the terms "run time" and "compile time" are situations in which a particular piece of code is being processed, not absolutes like in some static languages. If we evaluate (compile nil '(lambda ())), this is the compile function's run-time, but the lambda form's compile time: both times are happening at the same time.

The entire language is available in all situations. When you build a self-contained executable, that image contains not only support for expanding macros, but for compiling code. Your Lisp application can call compile-file to compile Lisp source to object form, and load to load the resulting object code.

The process of removing garbage, and unused functionality from a Lisp application image to make it smaller is called "tree shaking". If you don't want the compiler or macro expander in your application, find out whether/how they can be removed with your implementation's tree shaking support.

Related