Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why aren't lisp macros eagerly expanded by default?

Say I have macros foo and bar. If I write (foo (bar)) my understanding is that in most (all?) lisps foo is going to be given '(bar), not whatever bar would have expanded to had it been expanded first. Some lisps have something like local-expand where the implementation of foo can explicitly request the expansion of its argument before continuing, but why isn't that the default? It seems more natural to me. Is this an accident of history or is there a strong reason to do it the way most lisps do it?

I've been noticing in Rust that I want the macros to work this way. I'd like to be able to wrap a bunch of declarations inside a macro call so that the macro can then crawl the declarations and generate reflection information. But if I use a macro to generate the definitions that I want crawled, my macro wrapping the declarations sees the macro invocations that would generate the declarations rather than the actual declarations.

like image 947
Joseph Garvin Avatar asked Jul 01 '26 20:07

Joseph Garvin


1 Answers

If I write (foo (bar)) my understanding is that in most (all?) lisps foo is going to be given '(bar), not whatever bar would have expanded to had it been expanded first.

That would restrict Lisp such that (bar) would need to be something that can be expanded -> probably something which is written in the Lisp language.

Lisp developers would like to see macros where the inner stuff can be a completely new language with different syntax rules. For example something, where FOO does not expand it's subforms, but transpiles/compiles a fully/partially different language to Lisp. Something which does not have the usual prefix expression syntax:

Examples

(postfix (a b +) sin)

  -> (sin (+ a b))

Here the + in the macro form is not the infix +.

or

(query-all (person name)
   where (person = "foo") in database DB)

Lisp macros don't work on language parse trees, but arbitrary, possibly nested, s-expressions. Those don't need to be valid Lisp code outside of that macro -> don't need to follow the usual syntax / semantics.

Common Lisp has the function MACROEXPAND and MACROEXPAND-1, such that the outer macro can expand inner code if it wants to do it during its own macro expansion:

CL-USER 26 > (defmacro bar (a) `(* ,a ,a))
BAR

CL-USER 27 > (bar 10)
100

CL-USER 28 > (defmacro foo (a &environment e)
               (let ((f (macroexpand a e)))
                 (print (list a '-> f))
                 `(+ ,(second f) ,(third f))))
FOO

CL-USER 29 > (foo (bar 10))

((bar 10) -> (* 10 10))
20

In above macro, if FOO would see only an expanded form, it could not print both the source and the expansion.

This works also with scoped macros. Here the BAR macro gets locally redefined and the MACROEXPAND generates different code inside FOO for the same form:

CL-USER 30 > (macrolet ((bar (a)
                          `(expt ,a ,a)))
               (foo (bar 10)))

((bar 10) -> (EXPT 10 10))
20
like image 127
Rainer Joswig Avatar answered Jul 03 '26 14:07

Rainer Joswig