Macros in Common Lisp

advertisement
Macros and general code
walkers in Lisp:
how useful! or, how useful?
Ernst van Waning
evw@infometrics.nl
Overview
Short overview of macros in Lisp
Macros transform macro calls
Macros are not code transformers
In frustration I have sometimes thought
macros have a certain smell
Lisp has many specific code walkers
Q: can we make general code walkers?
Benelux-Lispers
Macros in Common Lisp
2
Macros in Lisp
Macros are replaced by their expansion
(expansion time)
The original call evaporates
After expansion, their code may be
evaluated (evaluation time)
Benelux-Lispers
Macros in Common Lisp
3
Expansion and evaluation
Arguments of macro calls are not
evaluated
At expansion time, appropriate error
messages do make sense
Evaluation is done after the macro
expanded
Benelux-Lispers
Macros in Common Lisp
4
Debugging macros
Macroexpand-1 expands your macro
once (i.e., applies the expander once)
Macroexpand expands your macro call
until it is no longer a macro call
With 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
5
Tracing expansions
Useful for debugging local macros
Useful 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
6
A model for macros
Lisp 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
7
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
8
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
9
A model for macros
The actual argument to an expander is
the entire macro call
Macros do not evaluate the arguments
of the macro call
The expander is really a function
retaining its lexical environment
Macros are not first-class values
Benelux-Lispers
Macros in Common Lisp
10
What are macros good for?
Macros control evaluation
Special 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
12
Macros compute during
expansion
(defmacro avg (&rest numbers)
`(/ (+ ,@numbers) ,(length numbers)))
Definers also compute during expansion
Defun, defmacro, &c. do a lot of
bookkeeping
Think of writing your own definers!
Benelux-Lispers
Macros in Common Lisp
13
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 argument
There are many accessors with their
own setf-expanders
Benelux-Lispers
Macros in Common Lisp
14
Macros set up lexical bindings
(dotimes (i 10 i) (princ i))
With-macros
with-open-file, with-output-to-string &c.
Do-macros
do, dolist, dotimes, do-symbols &c.
Benelux-Lispers
Macros in Common Lisp
15
What more do we want?
Macros as code transformers
Suppose we want to have type-strict
expressions but without pain
A 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
17
Code transformation
Sums 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
18
Domain specific extensions
Lisp is an extensible programming language
Build applications as extensions to Lisp
Telescoping: a strategy to automatically
generate highly optimized domain specific
libraries
But how easy is it to write in Lisp
domain-specific semantic checks?
domain-specific optimizations?
Benelux-Lispers
Macros in Common Lisp
19
Term Rewriting Systems (TRS)
aka Code Walkers
Useful in automatic theorem
proving
Lisp has many code walkers:
Read, eval, print, compile
Cross-referencers, and more…
Lisp makes it easy to write specific ones
Benelux-Lispers
Macros in Common Lisp
20
A general code walker in Lisp?
What is a general code walker?
Minimizes effort to write specific ones
Traverses all forms, a fortiori special ones
…more…?
Integration with macros is possible
Change macroexpand-1
Surrounding code can be made visible
by means of a stack
Benelux-Lispers
Macros in Common Lisp
21
A general code walker in Lisp?
The infamous 24 special forms of Lisp:
Special forms implemented as macro
Macros implemented as special form
Traversal of implementation-specific
special forms
All special forms retrievable in any Lisp
We can check if they have a walker
Benelux-Lispers
Macros in Common Lisp
22
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
23
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 solve
Would it really help you write your code
walkers?
Specific code walkers, however, remain
very useful
Benelux-Lispers
Macros in Common Lisp
24
Download