1
0
Fork 0
cl-sites/lispcookbook.github.io/cl-cookbook/macros.html

811 lines
45 KiB
HTML
Raw Normal View History

2023-10-25 11:23:21 +02:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Macros</title>
<meta charset="utf-8">
<meta name="description" content="A collection of examples of using Common Lisp">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<link rel="icon" href=
"assets/cl-logo-blue.png"/>
<link rel="stylesheet" href=
"assets/style.css">
<script type="text/javascript" src=
"assets/highlight-lisp.js">
</script>
<script type="text/javascript" src=
"assets/jquery-3.2.1.min.js">
</script>
<script type="text/javascript" src=
"assets/jquery.toc/jquery.toc.min.js">
</script>
<script type="text/javascript" src=
"assets/toggle-toc.js">
</script>
<link rel="stylesheet" href=
"assets/github.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1 id="title-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Macros</h1>
<div id="logo-container">
<a href="index.html">
<img id="logo" src="assets/cl-logo-blue.png"/>
</a>
<div id="searchform-container">
<form onsubmit="duckSearch()" action="javascript:void(0)">
<input id="searchField" type="text" value="" placeholder="Search...">
</form>
</div>
<div id="toc-container" class="toc-close">
<div id="toc-title">Table of Contents</div>
<ul id="toc" class="list-unstyled"></ul>
</div>
</div>
<div id="content-container">
<h1 id="title-non-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Macros</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
2024-05-15 18:18:38 +02:00
<!-- <p class="announce"> -->
<!-- 📢 🤶 ⭐ -->
<!-- <a style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPY-XMAS2023" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover our contributor's Lisp course with this Christmas coupon.</a> -->
<!-- <strong> -->
<!-- Recently added: 18 videos on MACROS. -->
<!-- </strong> -->
<!-- <a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</a>. -->
<!-- </p> -->
<p class="announce">
📢 New videos: <a href="https://www.youtube.com/watch?v=h_noB1sI_e8">web dev demo part 1</a>, <a href="https://www.youtube.com/watch?v=xnwc7irnc8k">dynamic page with HTMX</a>, <a href="https://www.youtube.com/watch?v=Zpn86AQRVN8">Weblocks demo</a>
</p>
2023-10-25 11:23:21 +02:00
<p class="announce-neutral">
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
</p>
<div id="content"
<p>The word <em>macro</em> is used generally in computer science to mean a syntactic extension to a programming language. (Note: The name comes from the word “macro-instruction,” which was a useful feature of many second-generation assembly languages. A macro-instruction looked like a single instruction, but expanded into a sequence of actual instructions. The basic idea has since been used many times, notably in the C preprocessor. The name “macro” is perhaps not ideal, since it connotes nothing relevant to what it names, but were stuck with it.) Although many languages have a macro facility, none of them are as powerful as Lisps. The basic mechanism of Lisp macros is simple, but has subtle complexities, so learning your way around it takes a bit of practice.</p>
<h2 id="how-macros-work">How Macros Work</h2>
<p>A macro is an ordinary piece of Lisp code that operates on <em>another piece of putative Lisp code,</em> translating it into (a version closer to) executable Lisp. That may sound a bit complicated, so lets give a simple example. Suppose you want a version of <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_setq.htm"><code>setq</code></a> that sets two variables to the same value. So if you write</p>
<pre><code class="language-lisp">(setq2 x y (+ z 3))
</code></pre>
<p>when <code>z</code>=8 then both <code>x</code> and <code>y</code> are set to 11. (I cant think of any use for this, but its just an example.)</p>
<p>It should be obvious that we cant define <code>setq2</code> as a function. If <code>x</code>=50 and <code>y</code>=<em>-5</em>, this function would receive the values 50, <em>-5</em>, and 11; it would have no knowledge of what variables were supposed to be set. What we really want to say is, When you (the Lisp system) see:</p>
<pre><code class="language-lisp">(setq2 v1 v2 e)
</code></pre>
<p>then treat it as equivalent to:</p>
<pre><code class="language-lisp">(progn
(setq v1 e)
(setq v2 e))
</code></pre>
<p>Actually, this isnt quite right, but it will do for now. A macro allows us to do precisely this, by specifying a program for transforming the input pattern <code>(setq2 <i>v<sub>1</sub></i> <i>v<sub>2</sub></i> <i>e</i>)</code> into the output pattern <code>(progn ...)</code>.</p>
<h3 id="quote">Quote</h3>
<p>Heres how we could define the <code>setq2</code> macro:</p>
<pre><code class="language-lisp">(defmacro setq2 (v1 v2 e)
(list 'progn (list 'setq v1 e) (list 'setq v2 e)))
</code></pre>
<p>It takes as parameters two variables and one expression.</p>
<p>Then it returns a piece of code. In Lisp, because code is represented
as lists, we can simply return a list that represents code.</p>
<p>We also use the <em>quote</em>, a <em>special operator</em> (not a function nor a macro, but one of a few special operators forming the core of Lisp).</p>
<p>Each <em>quoted</em> object evaluates to itself, aka it is returned as is:</p>
<ul>
<li><code>(+ 1 2)</code> evaluates to <code>3</code> but <code>(quote (+ 1 2))</code> evaluates to <code>(+ 1 2)</code></li>
<li><code>(quote (foo bar baz))</code> evaluates to <code>(foo bar baz)</code></li>
<li><code>'</code> is a shortcut for <code>quote</code>: <code>(quote foo)</code> and <code>'foo</code> are equvalent - both evaluate to <code>foo</code>.</li>
</ul>
<p>So, our macro returns the following bits:</p>
<ul>
<li>the symbol <code>progn</code>,</li>
<li>a second list, that contains
<ul>
<li>the symbol <code>setq</code></li>
<li>the variable <code>v1</code>: note that the variable is not evaluated inside the macro!</li>
<li>the expression <code>e</code>: it is not evaluated either!</li>
</ul>
</li>
<li>a second list, with <code>v2</code>.</li>
</ul>
<p>We can use it like this:</p>
<pre><code class="language-lisp">(defparameter v1 1)
(defparameter v2 2)
(setq2 v1 v2 3)
;; 3
</code></pre>
<p>We can check, <code>v1</code> and <code>v2</code> were set to <code>3</code>.</p>
<h3 id="macroexpand">Macroexpand</h3>
<p>We must start writing a macro when we know what code we want to
generate. Once weve begun writing one, it becomes very useful to
check effectively what code does the macro generate. The function for
that is <code>macroexpand</code>. It is a function, and we give it some code, as
a list (so, we quote the code snippet we give it):</p>
<pre><code class="language-lisp">(macroexpand '(setq2 v1 v2 3))
;; (PROGN (SETQ V1 3) (SETQ V2 3))
;; T
</code></pre>
<p>Yay, our macro expands to the code we wanted!</p>
<p>More interestingly:</p>
<pre><code class="language-lisp">(macroexpand '(setq2 v1 v2 (+ z 3)))
;; (PROGN (SETQ V1 (+ z 3)) (SETQ V2 (+ z 3)))
;; T
</code></pre>
<p>We can confirm that our expression <code>e</code>, here <code>(+ z 3)</code>, was not
evaluated. We will see how to control the evaluation of arguments with
the comma: <code>,</code>.</p>
<h3 id="note-slime-tips">Note: Slime tips</h3>
<p>With Slime, you can call macroexpand by putting the cursor at
the left of the parenthesis of the s-expr to expand and call the function<code>M-x
slime-macroexpand-[1,all]</code>, or <code>C-c M-m</code>:</p>
<pre><code class="language-lisp">[|](setq2 v1 v2 3)
;^ cursor
; C-c M-m
; =&gt;
; (PROGN (SETQ V1 3) (SETQ V2 3))
</code></pre>
<p>Another tip: on a macro name, type <code>C-c C-w m</code> (or <code>M-x
slime-who-macroexpands</code>) to get a new buffer with all the places
where the macro was expanded. Then type the usual <code>C-c C-k</code>
(<code>slime-compile-and-load-file</code>) to recompile all of them.</p>
<h3 id="macros-vs-functions">Macros VS functions</h3>
<p>Our macro is very close to the following function definition:</p>
<pre><code class="language-lisp">(defun setq2-function (v1 v2 e)
(list 'progn (list 'setq v1 e) (list 'setq v2 e)))
</code></pre>
<p>If we evaluated <code>(setq2-function 'x 'y '(+ z 3))</code> (note that each
argument is <em>quoted</em>, so it isnt evaluated when we call the
function), we would get</p>
<pre><code class="language-lisp">(progn (setq x (+ z 3)) (setq y (+ z 3)))
</code></pre>
<p>This is a perfectly ordinary Lisp computation, whose sole point of interest is that its output is a piece of executable Lisp code. What <code>defmacro</code> does is create this function implicitly and make sure that whenever an expression of the form <code>(setq2 x y (+ z 3))</code> is seen, <code>setq2-function</code> is called with the pieces of the form as arguments, namely <code>x</code>, <code>y</code>, and <code>(+ z 3)</code>. The resulting piece of code then replaces the call to <code>setq2</code>, and execution resumes as if the new piece of code had occurred in the first place. The macro form is said to <em>expand</em> into the new piece of code.</p>
<h3 id="evaluation-context">Evaluation context</h3>
<p>This is all there is to it, except, of course, for the myriad subtle consequences. The main consequence is that <em>run time for the <code>setq2</code> macro</em> is <em>compile time for its context.</em> That is, suppose the Lisp system is compiling a function, and midway through it finds the expression <code>(setq2 x y (+ z 3))</code>. The job of the compiler is, of course, to translate source code into something executable, such as machine language or perhaps byte code. Hence it doesnt execute the source code, but operates on it in various mysterious ways. However, once the compiler sees the <code>setq2</code> expression, it must suddenly switch to executing the body of the <code>setq2</code> macro. As I said, this is an ordinary piece of Lisp code, which can in principle do anything any other piece of Lisp code can do. That means that when the compiler is running, the entire Lisp (run-time) system must be present.</p>
<p>Well stress this once more: at compile-time, you have the full language at your disposal.</p>
<p>Novices often make the following sort of mistake. Suppose that the <code>setq2</code> macro needs to do some complex transformation on its <code>e</code> argument before plugging it into the result. Suppose this transformation can be written as a Lisp procedure <code>some-computation</code>. The novice will often write:</p>
<pre><code class="language-lisp">(defmacro setq2 (v1 v2 e)
(let ((e1 (some-computation e)))
(list 'progn (list 'setq v1 e1) (list 'setq v2 e1))))
(defmacro some-computation (exp) ...) ;; _Wrong!_
</code></pre>
<p>The mistake is to suppose that once a macro is called, the Lisp system enters a “macro world,” so naturally everything in that world must be defined using <code>defmacro</code>. This is the wrong picture. The right picture is that <code>defmacro</code> enables a step into the <em>ordinary Lisp world</em>, but in which the principal object of manipulation is Lisp code. Once that step is taken, one uses ordinary Lisp function definitions:</p>
<pre><code class="language-lisp">(defmacro setq2 (v1 v2 e)
(let ((e1 (some-computation e)))
(list 'progn (list 'setq v1 e1) (list 'setq v2 e1))))
(defun some-computation (exp) ...) ;; _Right!_
</code></pre>
<p>One possible explanation for this mistake may be that in other languages, such as C, invoking a preprocessor macro <em>does</em> get you into a different world; you cant run an arbitrary C program. It might be worth pausing to think about what it might mean to be able to.</p>
<p>Another subtle consequence is that we must spell out how the arguments to the macro get distributed to the hypothetical behind-the-scenes function (called <code>setq2-function</code> in my example). In most cases, it is easy to do so: In defining a macro, we use all the usual <code>lambda</code>-list syntax, such as <code>&amp;optional</code>, <code>&amp;rest</code>, <code>&amp;key</code>, but what gets bound to the formal parameters are pieces of the macro form, not their values (which are mostly unknown, this being compile time for the macro form). So if we defined a macro thus:</p>
<pre><code class="language-lisp">(defmacro foo (x &amp;optional y &amp;key (cxt 'null)) ...)
</code></pre>
<p>then</p>
<ul>
<li>if we call it with <code>(foo a)</code>, the parameters values are: <code>x=a</code>, <code>y=nil</code>, <code>cxt=null</code>.</li>
<li>calling <code>(foo (+ a 1) (- y 1))</code> gives: <code>x=(+ a 1)</code>, <code>y=(- y 1)</code>, <code>cxt=null</code>.</li>
<li>and <code>(foo a b :cxt (zap zip))</code> gives: <code>x=a</code>, <code>y=b</code>, <code>cxt=(zap zip)</code>.</li>
</ul>
<p>Note that the values of the variables are the actual expressions <code>(+ a 1)</code> and <code>(zap zip)</code>. There is no requirement that these expressions values be known, or even that they have values. The macro can do anything it likes with them. For instance, heres an even more useless variant of <code>setq</code>: <code>(setq-reversible <i>e<sub>1</sub></i> <i>e<sub>2</sub></i> <i>d</i>)</code> behaves like <code>(setq <i>e<sub>1</sub></i> <i>e<sub>2</sub></i>)</code> if <i>d=</i><code>:normal</code>, and behaves like <code>(setq <i>e<sub>2</sub></i> <i>e<sub>1</sub></i>)</code> if <em>d=</em><code>:backward</code>. It could be defined thus:</p>
<pre><code class="language-lisp">(defmacro setq-reversible (e1 e2 direction)
(case direction
(:normal (list 'setq e1 e2))
(:backward (list 'setq e2 e1))
(t (error "Unknown direction: ~a" direction))))
</code></pre>
<p>Heres how it expands:</p>
<pre><code class="language-lisp">(macroexpand '(setq-reversible x y :normal))
(SETQ X Y)
T
(macroexpand '(setq-reversible x y :backward))
(SETQ Y X)
T
</code></pre>
<p>And with a wrong direction:</p>
<pre><code class="language-lisp">(macroexpand '(setq-reversible x y :other-way-around))
</code></pre>
<p>We get an error and are prompted into the debugger!</p>
<p>Well see the backquote and comma mechanism in the next section, but
heres a fix:</p>
<pre><code class="language-lisp">(defmacro setq-reversible (v1 v2 direction)
(case direction
(:normal (list 'setq v1 v2))
(:backward (list 'setq v2 v1))
(t `(error "Unknown direction: ~a" ,direction))))
;; ^^ backquote ^^ comma: get the value inside the backquote.
(macroexpand '(SETQ-REVERSIBLE v1 v2 :other-way-around))
;; (ERROR "Unknown direction: ~a" :OTHER-WAY-AROUND)
;; T
</code></pre>
<p>Now when we call <code>(setq-reversible v1 v2 :other-way-around)</code> we still get the
error and the debugger, but at least not when using <code>macroexpand</code>.</p>
<p><a name="2-backquote"></a></p>
<h2 id="backquote-and-comma">Backquote and comma</h2>
<p>Before taking another step, we need to introduce a piece of Lisp notation that is indispensable to defining macros, even though technically it is quite independent of macros. This is the <em>backquote facility</em>. As we saw above, the main job of a macro, when all is said and done, is to define a piece of Lisp code, and that means evaluating expressions such as <code>(list 'prog (list 'setq ...) ...)</code>. As these expressions grow in complexity, it becomes hard to read them and write them. What we find ourselves wanting is a notation that provides the skeleton of an expression, with some of the pieces filled in with new expressions. Thats what backquote provides. Instead of the <code>list</code> expression given above, one writes</p>
<pre><code class="language-lisp"> `(progn (setq ,v1 ,e) (setq ,v2 ,e))
;;^ backquote ^ ^ ^ ^ commas
</code></pre>
<p>The backquote (`) character signals that in the expression that follows, every subexpression <em>not</em> preceded by a comma is to be quoted, and every subexpression preceded by a comma is to be evaluated.</p>
<p>You can think of it, and use it, as data interpolation:</p>
<pre><code class="language-lisp">`(v1 = ,v1) ;; =&gt; (V1 = 3)
</code></pre>
<p>Thats mostly all there is to backquote. There are just two extra items to point out.</p>
<h4 id="comma-splice-">Comma-splice ,@</h4>
<p>First, if you write “<code>,@e</code>” instead of “<code>,e</code>” then the value of <em>e</em> is <em>spliced</em> (or “joined”, “combined”, “interleaved”) into the result. So if <code>v</code> equals <code>(oh boy)</code>, then</p>
<pre><code class="language-lisp">`(zap ,@v ,v)
</code></pre>
<p>evaluates to</p>
<pre><code class="language-lisp">(zap oh boy (oh boy))
;; ^^^^^ elements of v (two elements), spliced.
;; ^^ v itself (a list)
</code></pre>
<p>The second occurrence of <code>v</code> is replaced by its value. The first is replaced by the elements of its value. If <code>v</code> had had value <code>()</code>, it would have disappeared entirely: the value of <code>(zap ,@v ,v)</code> would have been <code>(zap ())</code>, which is the same as <code>(zap nil)</code>.</p>
<h4 id="quote-comma-">Quote-comma ,</h4>
<p>When we are inside a backquote context and we want to print an
expression literally, we have no choice but to use the combination of
quote and comma:</p>
<pre><code class="language-lisp">(defmacro explain-exp (exp)
`(format t "~S = ~S" ',exp ,exp))
;; ^^
(explain-exp (+ 2 3))
;; (+ 2 3) = 5
</code></pre>
<p>See by yourself:</p>
<pre><code class="language-lisp">;; Defmacro with no quote at all:
(defmacro explain-exp (exp)
(format t "~a = ~a" exp exp))
(explain-exp v1)
;; V1 = V1
;; OK, with a backquote and a comma to get the value of exp:
(defmacro explain-exp (exp)
;; WRONG example
`(format t "~a = ~a" exp ,exp))
(explain-exp v1)
;; =&gt; error: The variable EXP is unbound.
;; We then must use quote-comma:
(defmacro explain-exp (exp)
`(format t "~a = ~a" ',exp ,exp))
(explain-exp (+ 1 2))
;; (+ 1 2) = 3
</code></pre>
<h4 id="nested-backquotes">Nested backquotes</h4>
<p>Second, one might wonder what happens if a backquote expression occurs inside another backquote. The answer is that the backquote becomes essentially unreadable and unwriteable; using nested backquote is usually a tedious debugging exercise. The reason, in my not-so-humble opinion, is that backquote is defined wrong. A comma pairs up with the innermost backquote when the default should be that it pairs up with the outermost. But this is not the place for a rant; consult your favorite Lisp reference for the exact behavior of nested backquote plus some examples.</p>
<h4 id="building-lists-with-backquote">Building lists with backquote</h4>
<p>One problem with backquote is that once you learn it you tend to use for every list-building occasion. For instance, you might write</p>
<pre><code class="language-lisp">(mapcan (lambda (x)
(cond ((symbolp x) `((,x)))
((&gt; x 10) `(,x ,x))
(t '())))
some-list)
</code></pre>
<p>which yields <code>((a) 15 15)</code> when <code>some-list</code> = <code>(a 6 15)</code>. The problem is that <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm"><code>mapcan</code></a> destructively alters the results returned by the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_lambda.htm"><code>lambda</code></a>-expression. Can we be sure that the lists returned by that expression are “<a href="http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#fresh">fresh</a>,” that is, they are different (in the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_eq.htm"><code>eq</code></a> sense) from the structures returned on other calls of that <code>lambda</code> expression? In the present case, close analysis will show that they must be fresh, but in general backquote is not obligated to return a fresh list every time (whether it does or not is implementation-dependent). If the example above got changed to</p>
<pre><code class="language-lisp">(mapcan (lambda (x)
(cond ((symbolp x) `((,x)))
((&gt; x 10) `(,x ,x))
((&gt;= x 0) `(low))
(t '())))
some-list)
</code></pre>
<p>then backquote may well treat <code>(low)</code> as if it were
<code>'(low)</code>; the list will be allocated at load time, and every time the
<code>lambda</code> is evaluated, that same chunk of storage will be returned. So
if we evaluate the expression with <code>some-list</code> = <code>(a 6 15)</code>, we will
get <code>((a) low 15 15)</code>, but as a side effect the constant <code>(low)</code> will
get clobbered to become <code>(low 15 15)</code>. If we then evaluate the
expression with, say, <code>some-list</code> = <code>(8 oops)</code>, the result will be
<code>(low 15 15 (oops))</code>, and now the “constant” that started off as
<code>'(low)</code> will be <code>(low 15 15 (oops))</code>. (Note: The bug exemplified here
takes other forms, and has often bit newbies - as well as experienced
programmers - in the ass. The general form is that a constant list is
produced as the value of something that is later destructively
altered. The first line of defense against this bug is never to
destructively alter any list. For newbies, this is also the last line
of defense. For those of us who imagine were more sophisticated, the
next line of defense is to think very carefully any time you use
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_nconc.htm"><code>nconc</code></a> or <code>mapcan</code>).</p>
<p>To fix the bug, you can write <code>(map 'list ...)</code> instead of <code>mapcan</code>. However, if you are determined to use <code>mapcan</code>, write the expression this way:</p>
<pre><code class="language-lisp">(mapcan (lambda (x)
(cond ((symbolp x) (list `(,x)))
((&gt; x 10) (list x x))
((&gt;= x 0) (list 'low))
(t '())))
some-list)
</code></pre>
<p>My personal preference is to use backquote <em>only</em> to build S-expressions, that is, hierarchical expressions that consist of symbols, numbers, and strings, and that are not conceptualized as changing in length. For instance, I would never write</p>
<pre><code class="language-lisp">(setq sk `(,x ,@sk))
</code></pre>
<p>If <code>sk</code> is being used as a stack, that is, its going to be <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_pop.htm"><code>pop</code></a>ped in the normal course of things, I would write <code>(push x sk)</code>. If not, I would write <code>(setq sk (cons x sk))</code>.</p>
<p><a name="LtohTOCentry-3"></a></p>
<h2 id="getting-macros-right">Getting Macros Right</h2>
<p>I said in the first section that my definition of <code>setq2</code> wasnt quite right, and now its time to fix it.</p>
<p>Suppose we write <code>(setq2 x y (+ x 2))</code>, when <code>x</code><em>=8</em>. Then according to the definition given above, this form will expand into</p>
<pre><code class="language-lisp">(progn
(setq x (+ x 2))
(setq y (+ x 2)))
</code></pre>
<p>so that <code>x</code> will have value 10 and <code>y</code> will have value 12. Indeed, heres its macroexpansion:</p>
<pre><code class="language-lisp">(macroexpand '(setq2 x y (+ x 2)))
;;(PROGN (SETQ X (+ X 2)) (SETQ Y (+ X 2)))
</code></pre>
<p>Chances are that isnt what the macro is expected to do (although you never know). Another problematic case is <code>(setq2 x y (pop l))</code>, which causes <code>l</code> to be popped twice; again, probably not right.</p>
<p>The solution is to evaluate the expression <code>e</code> just once, save it in a temporary variable, and then set <code>v1</code> and <code>v2</code> to it.</p>
<h3 id="gensym">Gensym</h3>
<p>To make temporary variables, we use the <code>gensym</code> function, which returns a fresh variable guaranteed to appear nowhere else. Here is what the macro should look like:</p>
<pre><code class="language-lisp">(defmacro setq2 (v1 v2 e)
(let ((tempvar (gensym)))
`(let ((,tempvar ,e))
(progn (setq ,v1 ,tempvar)
(setq ,v2 ,tempvar)))))
</code></pre>
<p>Now <code>(setq2 x y (+ x 2))</code> expands to</p>
<pre><code class="language-lisp">(let ((#:g2003 (+ x 2)))
(progn (setq x #:g2003) (setq y #:g2003)))
</code></pre>
<p>Here <code>gensym</code> has returned the symbol <code>#:g2003</code>, which prints in this funny way because it wont be recognized by the reader. (Nor is there any need for the reader to recognize it, since it exists only long enough for the code that contains it to be compiled.)</p>
<p>Exercise: Verify that this new version works correctly for the case <code>(setq2 x y (pop l1))</code>.</p>
<p>Exercise: Try writing the new version of the macro without using backquote. If you cant do it, you have done the exercise correctly, and learned what backquote is for!</p>
<p>The moral of this section is to think carefully about which expressions in a macro get evaluated and when. Be on the lookout for situations where the same expression gets plugged into the output twice (as <code>e</code> was in my original macro design). For complex macros, watch out for cases where the order that expressions are evaluated differs from the order in which they are written. This is sure to trip up some user of the macro - even if you are the only user.<a name="LtohTOCentry-4"></a></p>
<h2 id="what-macros-are-for">What Macros are For</h2>
<p>Macros are for making syntactic extensions to Lisp. One often hears it said that macros are a bad idea, that users cant be trusted with them, and so forth. Balderdash. It is just as reasonable to extend a language syntactically as to extend it by defining your own procedures. It may be true that the casual reader of your code cant understand the code without seeing the macro definitions, but then the casual reader cant understand it without seeing function definitions either. Having <a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_defmet.htm"><code>defmethod</code></a>s strewn around several files contributes far more to unclarity than macros ever have, but thats a different diatribe.</p>
<p>Before surveying what sorts of syntactic extensions I have found useful, let me point out what sorts of syntactic extensions are generally <em>not</em> useful, or best accomplished using means other than macros. Some novices think macros are useful for open-coding functions. So, instead of defining</p>
<pre><code class="language-lisp">(defun sqone (x)
(let ((y (+ x 1))) (* y y)))
</code></pre>
<p>they might define</p>
<pre><code class="language-lisp">(defmacro sqone (x)
`(let ((y (+ ,x 1))) (* y y)))
</code></pre>
<p>So that <code>(sqone (* z 13))</code> might expand into</p>
<pre><code class="language-lisp">(let ((y (+ (* z 13) 1)))
(* y y))
</code></pre>
<p>This is correct, but a waste of effort. For one thing, the amount of time saved is almost certainly negligible. If its really important that <code>sqone</code> be expanded inline, one can put <code>(declaim (inline sqone))</code> before <code>sqone</code> is defined (although the compiler is not obligated to honor this declaration). For another, once <code>sqone</code> is defined as a macro, it becomes impossible to write <code>(mapcar #'sqone ll)</code>, or to do anything else with it except call it.</p>
<p>But macros have a thousand and one legitimate uses. Why write <code>(lambda (x) ...)</code> when you can write <code>(\\ (x) ...)</code>? Just define <code>\\</code> as a macro: <code>(defmacro \\ (&amp;rest list) `(lambda ,@list))</code>.</p>
<p>Many people find <code>mapcar</code> and <code>mapcan</code> a bit too obscure, especially when used with large <code>lambda</code> expressions. Rather than write something like</p>
<pre><code class="language-lisp">(mapcar (lambda (x)
(let ((y (hairy-fun1 x))
(z (hairy-fun2 x)))
(dolist (y1 y)
(dolist (z1 z)
_... and further meaningless_
_space-filling nonsense..._
))))
list)
</code></pre>
<p>we might prefer to write</p>
<pre><code class="language-lisp">(for (x :in list)
(let ((y (hairy-fun1 x))
(z (hairy-fun2 x)))
(dolist (y1 y)
(dolist (z1 z)
_... and further meaningless_
_space-filling nonsense..._
))))
</code></pre>
<p>This macro might be defined thus:</p>
<pre><code class="language-lisp">(defmacro for (listspec exp)
;; ^^ listspec = (x :in list), a list of length 3.
;; ^^ exp = the rest of the code.
(cond
((and (= (length listspec) 3)
(symbolp (first listspec))
(eq (second listspec) ':in))
`(mapcar (lambda (,(first listspec))
,exp)
,(third listspec)))
(t (error "Ill-formed for spec: ~A" listspec)))))
</code></pre>
<p>(This is a simplified version of a macro by Chris Riesbeck.)</p>
<p>Its worth stopping for a second to discuss the role the keyword <code>:in</code> plays in this macro. It serves as a sort of “local syntax marker,” in that it has no meaning as far as Lisp is concerned, but does serve as a syntactic guidepost for the macro itself. I will refer to these markers as <em>guide symbols</em>. (Here its job may seem trivial, but if we generalized the <code>for</code> macro to allow multiple list arguments and an implicit <code>progn</code> in the body the <code>:in</code>s would be crucial in telling us where the arguments stopped and the body began.)</p>
<p>It is not strictly necessary for the guide symbols of a macro to be in the <a href="http://www.lispworks.com/documentation/HyperSpec/Body/11_abc.htm">keyword package</a>, but it is a good idea, for two reasons. First, they highlight to the reader that something idiosyncratic is going on. A form like <code>(for ((x in (foobar a b 'oof))) (something-hairy x (list x)))</code> looks a bit wrong already, because of the double parentheses before the <code>x</code>. But using “<code>:in</code>” makes it more obvious.</p>
<p>Second, notice that I wrote <code>(eq (second listspec) ':in)</code> in the macro definition to check for the presence of the guide symbol. If I had used <code>in</code> instead, I would have had to think about which package <em>my</em> <code>in</code> lives in and which package the macro users <code>in</code> lives in. One way to avoid trouble would be to write</p>
<pre><code class="language-lisp">(and (symbolp (second listspec))
(eq (intern (symbol-name (second listspec))
:keyword)
':in))
</code></pre>
<p>Another would be to write</p>
<pre><code class="language-lisp">(and (symbolp (second listspec))
(string= (symbol-name (second listspec)) (symbol-name 'in)))
</code></pre>
<p>which neither of which is particularly clear or aesthetic. The keyword package is there to provide a home for symbols whose home is not per se relevant to anything; you might as well use it. (Note: In ANSI Lisp, I could have written <code>"IN"</code> instead of <code>(symbol-name 'in)</code>, but there are Lisp implementations that do not convert symbols names to uppercase. Since I think the whole uppercase conversion idea is an embarrassing relic, I try to write code that is portable to those implementations.)</p>
<p>Lets look at another example, both to illustrate a nice macro, and to provide an auxiliary function for some of the discussion below. One often wants to create new symbols in Lisp, and <code>gensym</code> is not always adequate for building them. Here is a description of an alternative facility called <code>build-symbol</code>:</p>
<blockquote>
<p><code>(build-symbol [(:package <em>p</em>)] <em>-pieces-</em>)</code> builds a symbol by concatenating the given <em>pieces</em> and interns it as specified by <em>p</em>. For each element of <em>pieces</em>, if it is a …</p>
<ul>
<li>… string: The string is added to the new symbols name.</li>
<li>… symbol: The name of the symbol is added to the new symbols name.</li>
<li>… expression of the form <code>(:&lt; <em>e</em>)</code>: <em>e</em> should evaluate to a string, symbol, or number; the characters of the value of <em>e</em> (as printed by <code>princ</code>) are concatenated into the new symbols name.</li>
<li>… expression of the form <code>(:++ <em>p</em>)</code>: <em>p</em> should be a place expression (i.e., appropriate as the first argument to <code>setf</code>), whose value is an integer; the value is incremented by 1, and the new value is concatenated into the new symbols name.</li>
</ul>
<p>If the <code>:package</code> specification is omitted, it defaults to the value of <code>*package*</code>. If <em>p</em> is <code>nil</code>, the symbol is interned nowhere. Otherwise, it should evaluate to a package designator (usually, a keyword whose name is the same of a package).</p>
</blockquote>
<p>For example, <code>(build-symbol (:&lt; x) "-" (:++ *x-num*))</code>, when <code>x</code> = <code>foo</code> and <code>*x-num*</code> = 8, sets <code>*x-num*</code> to 9 and evaluates to <code>FOO-9</code>. If evaluated again, the result will be <code>FOO-10</code>, and so forth.</p>
<p>Obviously, <code>build-symbol</code> cant be implemented as a function; it has to be a macro. Here is an implementation:</p>
<pre><code class="language-lisp">(defmacro build-symbol (&amp;rest list)
(let ((p (find-if (lambda (x)
(and (consp x)
(eq (car x) ':package)))
list)))
(when p
(setq list (remove p list)))
(let ((pkg (cond ((eq (second p) 'nil)
nil)
(t `(find-package ',(second p))))))
(cond (p
(cond (pkg
`(values (intern ,(symstuff list) ,pkg)))
(t
`(make-symbol ,(symstuff list)))))
(t
`(values (intern ,(symstuff list))))))))
(defun symstuff (list)
`(concatenate 'string
2024-01-12 09:23:31 +01:00
,@(for (x :in list)
(cond ((stringp x)
`',x)
((atom x)
`',(format nil "~a" x))
((eq (car x) ':&lt;)
`(format nil "~a" ,(second x)))
((eq (car x) ':++)
`(format nil "~a" (incf ,(second x))))
(t
`(format nil "~a" ,x))))))
2023-10-25 11:23:21 +02:00
</code></pre>
<p>(Another approach would be have <code>symstuff</code> return a single call of the form <code>(format nil <em>format-string -forms-</em>)</code>, where the <em>forms</em> are derived from the <em>pieces</em>, and the <em>format-string</em> consists of interleaved ~as and strings.)</p>
<p>Sometimes a macro is needed only temporarily, as a sort of syntactic scaffolding. Suppose you need to define 12 functions, but they fall into 3 stereotyped groups of 4:</p>
<pre><code class="language-lisp">(defun make-a-zip (y z)
(vector 2 'zip y z))
(defun test-whether-zip (x)
(and (vectorp x) (eq (aref x 1) 'zip)))
(defun zip-copy (x) ...)
(defun zip-deactivate (x) ...)
(defun make-a-zap (u v w)
(vector 3 'zap u v w))
(defun test-whether-zap (x) ...)
(defun zap-copy (x) ...)
(defun zap-deactivate (x) ...)
(defun make-a-zep ()
(vector 0 'zep))
(defun test-whether-zep (x) ...)
(defun zep-copy (x) ...)
(defun zep-deactivate (x) ...)
</code></pre>
<p>Where the omitted pieces are the same in all similarly named functions. (That is, the “…” in <code>zep-deactivate</code> is the same code as the “…” in <code>zip-deactivate</code>, and so forth.) Here, for the sake of concreteness, if not plausibility, <code>zip</code>, <code>zap</code>, and <code>zep</code> are behaving like odd little data structures. The functions could be rather large, and it would get tedious keeping them all in sync as they are debugged. An alternative would be to use a macro:</p>
<pre><code class="language-lisp">(defmacro odd-define (name buildargs)
`(progn (defun ,(build-symbol make-a- (:&lt; name))
,buildargs
(vector ,(length buildargs) ',name ,@buildargs))
(defun ,(build-symbol test-whether- (:&lt; name)) (x)
(and (vectorp x) (eq (aref x 1) ',name))
(defun ,(build-symbol (:&lt; name) -copy) (x)
...)
(defun ,(build-symbol (:&lt; name) -deactivate) (x)
...))))
(odd-define zip (y z))
(odd-define zap (u v w))
(odd-define zep ())
</code></pre>
<p>If all the uses of this macro are collected in this one place, it might be clearer to make it a local macro using <a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm">macrolet</a>:</p>
<pre><code class="language-lisp">(macrolet ((odd-define (name buildargs)
2024-01-12 09:23:31 +01:00
`(progn
(defun ,(build-symbol make-a- (:&lt; name))
,buildargs
(vector ,(length buildargs)
',name
,@buildargs))
(defun ,(build-symbol test-whether- (:&lt; name))
(x)
(and (vectorp x) (eq (aref x 1) ',name))
(defun ,(build-symbol (:&lt; name) -copy) (x)
...)
(defun ,(build-symbol (:&lt; name) -deactivate) (x)
...)))))
2023-10-25 11:23:21 +02:00
(odd-define zip (y z))
(odd-define zap (u v w))
(odd-define zep ()))
</code></pre>
<p>Finally, macros are essential for defining “command languages.” A <em>command</em> is a function with a short name for use by users in interacting with Lisps read-eval-print loop. A short name is useful and possible because we want it to be easy to type and we dont care much whether the name clashes some other command; if two command names clash, we can change one of them.</p>
<p>As an example, lets define a little command language for debugging macros. (You may actually find this useful.) There are just two commands, <code>ex</code> and <code>fi</code>. They keep track of a “current form,” the thing to be macro-expanded or the result of such an expansion:</p>
<ol>
<li><code>(ex [<em>form</em>])</code>: Apply <code>macroexpand-1</code> to <em>form</em> (if supplied) or the current form, and make the result the current form. Then pretty-print the current form.</li>
<li><code>(fi <em>s</em> [<em>k</em>])</code>: Find the <em>k</em>th subexpression of the current form whose <code>car</code> is <em>s</em>. (<em>k</em> defaults to 0.) Make that subexpression the current form and pretty-print it.</li>
</ol>
<p>Suppose youre trying to debug a macro <code>hair-squared</code> that expands into something complex containing a subform that is itself a macro form beginning with the symbol <code>odd-define</code>. You suspect there is a bug in the subform. You might issue the following commands:</p>
<pre><code class="language-lisp">(ex (hair-squared ...))
(PROGN (DEFUN ...)
(ODD-DEFINE ZIP (U V W))
...)
(fi odd-define)
(ODD-DEFINE ZIP (U V W))
(ex)
(PROGN (DEFUN MAKE-A-ZIP (U V W) ...)
...)
</code></pre>
<p>Once again, it is clear that <code>ex</code> and <code>fi</code> cannot be functions, although they could easily be made into functions if we were willing to type a quote before their arguments. But using “quote” often seems inappropriate in commands. For one thing, having to type it is a nuisance in a context where we are trying to save keystrokes, especially if the argument in question is always quoted. For another, in many cases it just seems inappropriate. If we had a command that took a symbol as one of its arguments and set it to a value, it would just be strange to write <code>(<em>command</em> 'x ...)</code> instead of <code>(<em>command</em> x ...)</code>, because we want to think of the command as a variant of <code>setq</code>.</p>
<p>Here is how <code>ex</code> and <code>fi</code> might be defined:</p>
<pre><code class="language-lisp">(defvar *current-form*)
(defmacro ex (&amp;optional (form nil form-supplied))
`(progn
(pprint (setq *current-form*
(macroexpand-1
,(cond (form-supplied
`',form)
(t '*current-form*)))))
(values)))
(defmacro fi (s &amp;optional (k 0))
`(progn
(pprint (setq *current-form*
(find-nth-occurrence ',s *current-form* ,k)))
(values)))
</code></pre>
<p>The <code>ex</code> macro expands to a form containing a call to <code>macroexpand-1</code>, a built-in function that does one step of macro expansion to a form whose <code>car</code> is the name of a macro. (If given some other form, it returns the form unchanged.) <code>pprint</code> is a built-in function that pretty-prints its argument. Because we are using <code>ex</code> and <code>fi</code> at a read-eval-print loop, any value returned by their expansions will be printed. Here the expansion is executed for side effect, so we arrange to return no values at all by having the expansion return <code>(values)</code>.</p>
<p>In some Lisp implementations, read-eval-print loops routinely print results using <code>pprint</code>. In those implementations we could simplify <code>ex</code> and <code>fi</code> by having them print nothing, but just return the value of <code>*current-form*</code>, which the read-eval-print loop will then print prettily. Use your judgment.</p>
<p>I leave the definition of <code>find-nth-occurrence</code> as an exercise. You might also want to define a command that just sets and prints the current form: <code>(cf <em>e</em>)</code>.</p>
<p>One caution: In general, command languages will consist of a mixture of macros and functions, with convenience for their definer (and usually sole user) being the main consideration. If a command seems to “want” to evaluate some of its arguments sometimes, you have to decide whether to define two (or more) versions of it, or just one, a function whose arguments must be quoted to prevent their being evaluated. For the <code>cf</code> command mentioned in the previous paragraph, some users might prefer <code>cf</code> to be a function, some a macro.</p>
<h2 id="see-also">See also</h2>
<ul>
<li>
<p><a href="https://medium.com/@MartinCracauer/a-gentle-introduction-to-compile-time-computing-part-1-d4d96099cea0">A gentle introduction to Compile-Time Computing — Part 1</a></p>
</li>
<li>
<p><a href="https://medium.com/@MartinCracauer/a-gentle-introduction-to-compile-time-computing-part-3-scientific-units-8e41d8a727ca">Safely dealing with scientific units of variables at compile time (a gentle introduction to Compile-Time Computing — part 3)</a></p>
</li>
<li>
<p>The following video, from the series <a href="https://www.youtube.com/user/CBaggers/playlists">“Little bits of
Lisp”</a> by
<a href="https://github.com/cbaggers/">cbaggers</a>, is a two hours long talk on
macros, showing simple to advanced concepts such as compiler macros:
<a href="https://www.youtube.com/watch?v=ygKXeLKhiTI">https://www.youtube.com/watch?v=ygKXeLKhiTI</a>
It also shows how to manipulate macros (and their expansion) in Emacs.</p>
</li>
</ul>
<p><a href="https://www.youtube.com/watch?v=ygKXeLKhiTI"><img src="assets/youtube-little-bits-lisp.jpg" alt="video" /></a></p>
<ul>
<li>the article “Reader macros in Common Lisp”: https://lisper.in/reader-macros</li>
</ul>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/macros.md">macros.md</a>
</p>
</div>
<script type="text/javascript">
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
</script>
<div>
<footer class="footer">
<hr/>
&copy; 2002&ndash;2023 the Common Lisp Cookbook Project
<div>
2024-05-15 18:18:38 +02:00
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">our contributor's Common Lisp video course on Udemy</a>
2023-10-25 11:23:21 +02:00
</div>
</footer>
</div>
<div id="toc-btn">T<br>O<br>C</div>
</div>
<script text="javascript">
HighlightLisp.highlight_auto({className: null});
</script>
<script type="text/javascript">
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField && searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2&kf=-1&ko=1&q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
</script>
<script async defer data-domain="lispcookbook.github.io/cl-cookbook" src="https://plausible.io/js/plausible.js"></script>
</body>
</html>