macros and general code walkers in lisp: how useful! or, how useful? ernst van waning
DESCRIPTION
Benelux-LispersMacros in Common Lisp3 Macros are replaced by their expansion (expansion time) The original call evaporates After expansion, their code may be evaluated (evaluation time) Macros in LispTRANSCRIPT
Benelux-Lispers Macros in Common Lisp 2
OverviewShort overview of macros in LispMacros transform macro callsMacros are not code transformers
In frustration I have sometimes thought macros have a certain smell
Lisp has many specific code walkersQ: can we make general code walkers?
Benelux-Lispers Macros in Common Lisp 3
Macros are replaced by their expansion (expansion time)The original call evaporatesAfter expansion, their code may be evaluated (evaluation time)
Macros in Lisp
Benelux-Lispers Macros in Common Lisp 4
Expansion and evaluationArguments of macro calls are not evaluatedAt expansion time, appropriate error messages do make senseEvaluation is done after the macro expanded
Benelux-Lispers Macros in Common Lisp 5
Debugging macrosMacroexpand-1 expands your macro once (i.e., applies the expander once)Macroexpand expands your macro call until it is no longer a macro callWith a non-macro they just return (values form ’nil) Expanding all macros in all subforms is a different game: code walker
Benelux-Lispers Macros in Common Lisp 6
Tracing expansionsUseful for debugging local macrosUseful for studying system macros
(defmacro trexp (form &environment env) (let ((*print-pretty* t)) (multiple-value-bind (expansion expanded-p) (macroexpand-1 form env) (cond (expanded-p (format *trace-output* "~&~S~%-m1->~%“
form) `(trexp ,expansion)) (t (format *trace-output* "~&~S~%~%" form) expansion)))))
Benelux-Lispers Macros in Common Lisp 7
A model for macrosLisp is explained using Lisp, so we explain Lisp’s macros with Lisp
(defmacro our-expander (name) `(get ,name 'expander))
Our-expander is an accessor, i.e., it can be set with setf
Benelux-Lispers Macros in Common Lisp 8
A model for macros(defun our-macro-call? (xpr)
(and (consp xpr) (our-expander (car xpr))))
(defun our-macroexpand-1 (xpr) (if (our-macro-call? xpr) (funcall (our-expander (car xpr)) xpr) xpr))
Benelux-Lispers Macros in Common Lisp 9
A model for macros(defmacro our-defmacro (name args &body body)
(let ((g (gensym))) ;unique symbol `(progn (setf (our-expander ',name) ;store (lambda (,g) (block ,name ;for return-from (destructuring-bind ,args
(cdr ,g) ,@body)))) ',name)))
Benelux-Lispers Macros in Common Lisp 10
A model for macrosThe actual argument to an expander is the entire macro callMacros do not evaluate the arguments of the macro callThe expander is really a function retaining its lexical environmentMacros are not first-class values
What are macros good for?
Benelux-Lispers Macros in Common Lisp 12
Macros control evaluationSpecial syntax
(dotimes (i 10 i) (princ i)) Implicit quoting
`(setf (our-expander ‘,name) …)Design your own control structures
(aif xpr pos zero neg)Multiple evaluations
(dotimes (i 10 i) (princ i))
Benelux-Lispers Macros in Common Lisp 13
Macros compute during expansion
(defmacro avg (&rest numbers) `(/ (+ ,@numbers) ,(length numbers)))
Definers also compute during expansionDefun, defmacro, &c. do a lot of bookkeepingThink of writing your own definers!
Benelux-Lispers Macros in Common Lisp 14
Macros as accessors(setf (our-expander name) new)~m~> (something like)(let ((#:g452 name)
(#:g453 'expander)(#:g455 new)) (inverse-get #:g452 #:g453 #:g455))
Setf looks “inside” its first argumentThere are many accessors with their own setf-expanders
Benelux-Lispers Macros in Common Lisp 15
Macros set up lexical bindings
(dotimes (i 10 i) (princ i))
With-macroswith-open-file, with-output-to-string &c.
Do-macrosdo, dolist, dotimes, do-symbols &c.
What more do we want?
Benelux-Lispers Macros in Common Lisp 17
Macros as code transformers
Suppose we want to have type-strict expressions but without painA macro scalar is fine:(scalar :e) –m-> 2.718281…
But what if I want this?(scalar (exp 1))-m-> (exp (scalar 1))-m-> (exp 1d0)-m-> 2.718281… ;at expansion time
Benelux-Lispers Macros in Common Lisp 18
Code transformationSums of squares of real numbers are non-negative (at least, in theory):
(scalar (sqrt ssq)) -m*-> (the (scalar 0) (sqrt (the (scalar 0) ssq)))
Can we prove that ssq is non-negative by expanding a macro?Macros do not allow arbitrary code transformations
Benelux-Lispers Macros in Common Lisp 19
Domain specific extensions
Lisp is an extensible programming languageBuild applications as extensions to LispTelescoping: a strategy to automatically generate highly optimized domain specific librariesBut how easy is it to write in Lisp
domain-specific semantic checks?domain-specific optimizations?
Benelux-Lispers Macros in Common Lisp 20
Term Rewriting Systems (TRS) aka Code WalkersUseful in automatic theorem provingLisp has many code walkers:
Read, eval, print, compileCross-referencers, and more…
Lisp makes it easy to write specific ones
Benelux-Lispers Macros in Common Lisp 21
A general code walker in Lisp?
What is a general code walker?Minimizes effort to write specific onesTraverses all forms, a fortiori special ones…more…?
Integration with macros is possibleChange macroexpand-1
Surrounding code can be made visible by means of a stack
Benelux-Lispers Macros in Common Lisp 22
A general code walker in Lisp?
The infamous 24 special forms of Lisp:Special forms implemented as macroMacros implemented as special formTraversal of implementation-specific special formsAll special forms retrievable in any Lisp
We can check if they have a walker
Benelux-Lispers Macros in Common Lisp 23
A general code walker in Lisp?
What about macros?What about system-macros?What about the portability of your walkers?
Benelux-Lispers Macros in Common Lisp 24
A general code walker in Lisp?
Looks like a clear idea, but is it?Even if the idea would be clear, there are real problems to solveWould it really help you write your code walkers?Specific code walkers, however, remain very useful