emacs.d/clones/lisp/stevelosh.com/blog/2016/08/playing-with-syntax/index.html
2022-10-07 15:47:14 +02:00

452 lines
No EOL
30 KiB
HTML

<!DOCTYPE html>
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='http://stevelosh.com/static/css/style.css' rel='stylesheet' type='text/css' /><link href='http://stevelosh.com/static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>Playing With Syntax / Steve Losh</title></head><body><header><a id='logo' href='http://stevelosh.com/'>Steve Losh</a><nav><a href='http://stevelosh.com/blog/'>Blog</a> - <a href='http://stevelosh.com/projects/'>Projects</a> - <a href='http://stevelosh.com/photography/'>Photography</a> - <a href='http://stevelosh.com/links/'>Links</a> - <a href='http://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>Playing With Syntax</a></h1><p class='date'>Posted on August 19th, 2016.</p><p>One of the things I love about Lisp is that it gives you the ability to change
and mold the syntax of the language to what you need. In this post I want to
look at the evolution of a little macro I've been playing around with for
a while now.</p>
<p>Mutation is a fundamental concept in most programming languages. Functional
programming may be beautiful, but mutation is still useful (and fast). In most
languages assignment is done with an <code>=</code> or <code>:=</code> operator:</p>
<pre><code><span class="code">x = 10</span></code></pre>
<p>In Common Lisp the operator is named <code>setf</code> instead of <code>=</code> for historical
reasons, and of course it's used in prefix notation:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">setf x 10</span>)</span></span></code></pre>
<p>Aside from the prefix ordering, Common Lisp's syntax is already a bit more
elegant because you can set arbitrarily many variables without repeating the
assignment operator over and over again:</p>
<pre><code><span class="code"><span class="comment">; Have to type out `=` three times
</span>x = 10
y = 20
z = 30
<span class="comment">; But only one `setf` required
</span><span class="paren1">(<span class="code">setf x 10
y 20
z 30</span>)</span></span></code></pre>
<p><code>setf</code> assigns values one-by-one, in-order. Common Lisp also has <code>psetf</code> which
is &quot;parallel <code>setf</code>&quot;. This evaluates all the values first, <em>then</em> sets all the
variables at once:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">a 10</span>)</span> <span class="paren3">(<span class="code">b 20</span>)</span></span>)</span>
<span class="paren2">(<span class="code">setf a 50
b <span class="paren3">(<span class="code">+ 1 a</span>)</span></span>)</span> <span class="comment">; =&gt; (50 51)
</span>
<span class="paren2">(<span class="code">psetf a 90
b <span class="paren3">(<span class="code">+ 1 a</span>)</span></span>)</span></span>)</span> <span class="comment">; =&gt; (90 52)</span></span></code></pre>
<p>Assignment is nice, but often you want to change the value of a variable by
taking its <em>current</em> value and computing something with it. If you're making
a ball that travels across the screen you might have something like this:</p>
<pre><code>def move_ball(ball, distance):
ball.x = ball.x + distance
(defun move-ball (ball distance)
(setf (slot-value ball 'x)
(+ (slot-value ball 'x)
distance)))</code></pre>
<p>If you find <code>slot-value</code> too wordy to type, you could make a macro to get rid of
it (and save yourself a quote too). <code>.</code> is already used for something else so
you'd need to pick a different character:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> $ <span class="paren2">(<span class="code">instance slot-symbol</span>)</span>
`<span class="paren2">(<span class="code">slot-value ,instance ',slot-symbol</span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> move-ball <span class="paren2">(<span class="code">ball distance</span>)</span>
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">$ ball x</span>)</span>
<span class="paren3">(<span class="code">+ <span class="paren4">(<span class="code">$ ball x</span>)</span>
distance</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>In practice, though, you rarely use <code>slot-value</code>. Instead you usually use
accessor functions:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> move-ball <span class="paren2">(<span class="code">ball distance</span>)</span>
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">ball-x ball</span>)</span>
<span class="paren3">(<span class="code">+ <span class="paren4">(<span class="code">ball-x ball</span>)</span>
distance</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>Anyway, back to assignment. What we wrote above <em>works</em>, but it's a bit
annoying to have to type the &quot;place&quot; that we're working with twice. To cut down
on this repetition many languages offer operators like <code>+=</code> and <code>++</code>:</p>
<pre><code>x = x + 10
x += 10
y = y + 1
y++</code></pre>
<p>Common Lisp's version of these is called <code>incf</code>.</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">incf x 10</span>)</span>
<span class="paren1">(<span class="code">incf y</span>)</span></span></code></pre>
<p>Notice how the s-expression syntax means we can use the same operator for both
of the forms, instead of needing separate <code>+=</code> and <code>++</code> operators.</p>
<p>Other languages often have similar &quot;in-place&quot; operators for other numeric
operations like <code>-=</code>, <code>--</code>, <code>*=</code>, and so on. Common Lisp has <code>decf</code> for
subtraction, but doesn't have versions for multiplication or division built in.
But we can add them with a few macros:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> mulf <span class="paren2">(<span class="code">place factor</span>)</span>
`<span class="paren2">(<span class="code">setf ,place <span class="paren3">(<span class="code">* ,place ,factor</span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmacro</span></i> divf <span class="paren2">(<span class="code">place divisor</span>)</span>
`<span class="paren2">(<span class="code">setf ,place <span class="paren3">(<span class="code">/ ,place ,divisor</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>Unfortunately these are not quite right — we've committed the cardinal
macro-writing sin of splicing in the <code>place</code> expression multiple times. If
<code>place</code> has side effects they'll happen more times than expected.</p>
<p>Luckily, Common Lisp has a macro that wraps up the boring process of handling
this for us. <code>define-modify-macro</code> lets us fix the problem:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-modify-macro</span></i> mulf <span class="paren2">(<span class="code">factor</span>)</span> *</span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">define-modify-macro</span></i> divf <span class="paren2">(<span class="code">divisor</span>)</span> /</span>)</span></span></code></pre>
<p>Now we've got versions of <code>*=</code> and <code>/=</code> in Common Lisp. While we're here we
should do things <em>right</em> and allow <code>divf</code> to be used with just a place, which
will mean &quot;set <code>place</code> to <code>1/place</code>&quot;:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-modify-macro</span></i> divf <span class="paren2">(<span class="code">&amp;optional divisor</span>)</span>
<span class="paren2">(<span class="code"><i><span class="symbol">lambda</span></i> <span class="paren3">(<span class="code">value divisor</span>)</span>
<span class="paren3">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren4">(<span class="code">null divisor</span>)</span>
<span class="paren4">(<span class="code">/ 1 value</span>)</span>
<span class="paren4">(<span class="code">/ value divisor</span>)</span></span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">x 10</span>)</span></span>)</span>
<span class="paren2">(<span class="code">divf x 2</span>)</span> <span class="comment">; =&gt; 5
</span> <span class="paren2">(<span class="code">divf x</span>)</span> <span class="comment">; =&gt; 1/5
</span> <span class="paren2">(<span class="code">divf x</span>)</span></span>)</span> <span class="comment">; =&gt; 5</span></span></code></pre>
<p>Note that we don't really need the <code>1</code> in <code>(/ 1 value)</code>, because Common Lisp's
<code>/</code> works the same way when you call it with a single argument. I'm not sure
what behavior would make sense for a unary <code>(mulf place)</code>, so let's ignore that
one and move on.</p>
<p>So far we've been talking about the idea of setting a variable to the result of
computing some function on its current value. We used <code>define-modify-macro</code> to
define some support for extra functions, but defining a mutator macro for every
function we might possibly want to use could get tedious. What if we generalize
this a bit more?</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-modify-macro</span></i> callf <span class="paren2">(<span class="code"><i><span class="symbol">function</span></i></span>)</span>
<span class="paren2">(<span class="code"><i><span class="symbol">lambda</span></i> <span class="paren3">(<span class="code">value <i><span class="symbol">function</span></i></span>)</span>
<span class="paren3">(<span class="code">funcall <i><span class="symbol">function</span></i> value</span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">x -1</span>)</span></span>)</span>
<span class="paren2">(<span class="code">callf x #'abs</span>)</span> <span class="comment">; =&gt; 1
</span> <span class="paren2">(<span class="code">callf x #'1+</span>)</span> <span class="comment">; =&gt; 2
</span> <span class="paren2">(<span class="code">callf x #'sin</span>)</span> <span class="comment">; =&gt; 0.9092974
</span> <span class="paren2">(<span class="code">callf x #'round</span>)</span> <span class="comment">; =&gt; 1
</span> <span class="paren2">(<span class="code">callf x #'-</span>)</span></span>)</span> <span class="comment">; =&gt; -1</span></span></code></pre>
<p>Now we've got something a bit higher-level. With <code>callf</code> we can mutate
a variable with a unary function. But what about functions that need more
arguments? Again, Common Lisp does the right thing and handles <code>&amp;rest</code>
parameters correctly in <code>define-modify-macro</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">define-modify-macro</span></i> callf <span class="paren2">(<span class="code"><i><span class="symbol">function</span></i> &amp;rest args</span>)</span>
<span class="paren2">(<span class="code"><i><span class="symbol">lambda</span></i> <span class="paren3">(<span class="code">value <i><span class="symbol">function</span></i> &amp;rest args</span>)</span>
<span class="paren3">(<span class="code">apply <i><span class="symbol">function</span></i> value args</span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">x -1</span>)</span></span>)</span>
<span class="paren2">(<span class="code">callf x #'abs</span>)</span> <span class="comment">; =&gt; 1
</span> <span class="paren2">(<span class="code">callf x #'+ 10</span>)</span> <span class="comment">; =&gt; 11
</span> <span class="paren2">(<span class="code">callf x #'* 2 4</span>)</span></span>)</span> <span class="comment">; =&gt; 88
</span>
<span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">foo <span class="paren4">(<span class="code">list 0 1 2 3</span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">callf foo #'reverse</span>)</span>
<span class="comment">; =&gt; (3 2 1 0)
</span>
<span class="paren2">(<span class="code">callf foo #'append <span class="paren3">(<span class="code">list -1 -2 -3</span>)</span></span>)</span></span>)</span>
<span class="comment">; =&gt; (3 2 1 0 -1 -2 -3)</span></span></code></pre>
<p>That's better. Now we can mutate a variable by applying a function to its
current value and some other arguments. This might come in handy when we don't
want to bother defining a full-blown modification macro just to use it once or
twice.</p>
<p>At this point we've got something similar to Michael Malis' <a href="http://malisper.me/2015/09/29/zap/">zap</a> macro.
A minor difference is for <code>zap</code> the order of the function and the place are
reversed. I believe his reason was that it makes the resulting form look more
like the function call that is conceptually being made:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">zap #'+ x 5</span>)</span>
<span class="comment">; ↓ ↓ ↓
</span><span class="paren1">(<span class="code">setf x <span class="paren2">(<span class="code"> + x 5 </span>)</span></span>)</span></span></code></pre>
<p>Unfortunately, because the place is no longer the first argument we can't use
<code>define-modify-macro</code> to handle the single-evaluation boilerplate for us any
more. Instead we need to use <code>get-setf-expander</code> and work with its results.
Malis' excellent <a href="http://malisper.me/2015/09/22/getting-places/">Getting Places</a> article explains how that works, and if you
want to fully understand the rest of the code in this post you should read that
one first.</p>
<p>The difference between <code>callf</code> and Malis' <code>zap</code> are just cosmetic. But they're
still not completely flexible. What if we want to call a function, but we need
the value to be an argument other than the first? Of course we could use
a <code>lambda</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">x <span class="paren4">(<span class="code">list <span class="keyword">:coffee</span> <span class="keyword">:soda</span> <span class="keyword">:tea</span></span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">callf x <span class="paren3">(<span class="code"><i><span class="symbol">lambda</span></i> <span class="paren4">(<span class="code">l</span>)</span> <span class="paren4">(<span class="code">remove <span class="keyword">:soda</span> l</span>)</span></span>)</span></span>)</span></span>)</span>
</span></code></pre>
<p>But that is <em>really</em> ugly. It would be nice if we had a way to specify which
argument to the function should be the current value. This is Lisp, so we can
pick whatever syntax we want. What should we choose? Often a good first step
is to look at how other languages do things. Clojure's anonymous function
syntax is one possibility:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">map #<span class="paren2">(<span class="code">+ 10 %</span>)</span> [1 2 3]</span>)</span>
<span class="comment">; =&gt; (11 12 13)</span></span></code></pre>
<p>Clojure uses the magic symbol <code>%</code> as a placeholder for the argument. We can
modify Malis' <code>zap</code> macro to do the same. We'll call it <code>zapf</code>, and we'll swap
the place back into the first argument like all the other modify macros, since
the aesthetic reason no longer holds. The changes are in bold:</p>
<pre><code>; Original version
(defmacro zap (fn place &amp;rest args)
(multiple-value-bind
(temps exprs stores store-expr access-expr)
(get-setf-expansion place)
`(let* (,@(mapcar #'list temps exprs)
(,(car stores)
(funcall ,fn ,access-expr ,@args)))
,store-expr)))
; New version
(defmacro <b>zapf</b> (<b>place fn</b> &amp;rest args)
(multiple-value-bind
(temps exprs stores store-expr access-expr)
(get-setf-expansion place)
`(let* (,@(mapcar #'list temps exprs)
(,(car stores)
(funcall ,fn <b>,@(substitute access-expr '% args)</b>)))
,store-expr)))
</code></pre>
<p>And now we can use <code>%</code> to say where the place's value should go:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren2">(<span class="code"><span class="paren3">(<span class="code">x <span class="paren4">(<span class="code">list <span class="keyword">:coffee</span> <span class="keyword">:soda</span> <span class="keyword">:tea</span></span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">zapf x #'remove <span class="keyword">:soda</span> %</span>)</span>
<span class="comment">; =&gt; (:coffee :tea)
</span>
<span class="paren2">(<span class="code">zapf x #'cons <span class="keyword">:water</span> %</span>)</span>
<span class="comment">; =&gt; (:water :coffee :tea)
</span>
<span class="paren2">(<span class="code">zapf x #'append % <span class="paren3">(<span class="code">list <span class="keyword">:cocoa</span> <span class="keyword">:chai</span></span>)</span></span>)</span></span>)</span>
<span class="comment">; =&gt; (:water :coffee :tea :cocoa :chai)</span></span></code></pre>
<p>Note that <code>substitute</code> will replace <em>all</em> occurrences of <code>%</code> with the access
expression, so we can do something like <code>(zapf x #'append % %)</code> if we want.</p>
<p>Some people will find this new version unpleasant, because it effectively
captures the <code>%</code> variable. It's not hygienic, by design. I personally don't
mind it, and I like the brevity it allows.</p>
<p>We're almost done, but I want to push things just a <em>bit</em> further. Let's
revisit the original example of the ball moving across the screen:</p>
<pre><code>def move_ball(ball, distance):
ball.x = ball.x + distance
(defun move-ball (ball distance)
(setf (ball-x ball)
(+ (ball-x ball) distance)))</code></pre>
<p>Of course we can simply use <code>+=</code> or <code>incf</code> here:</p>
<pre><code>def move_ball(ball, distance):
ball.x += distance
(defun move-ball (ball distance)
(incf (ball-x ball) distance))</code></pre>
<p>Suppose we also wanted to make the ball wrap around the screen when it flies off
one side. We can do this using modulus:</p>
<pre><code>def move_ball(ball, distance, screen_width):
ball.x += distance
ball.x %= screen_width
(defun move-ball (ball distance screen-width)
(incf (ball-x ball) distance)
(callf (ball-x ball) #'mod % screen-width))</code></pre>
<p>We could define <code>modf</code> if we wanted to make that second call a bit nicer. But
let's take a moment to reflect. Notice how we're back to having to type out
<code>(ball-x ball)</code> twice again. It's better than typing it four times, but can we
do better?</p>
<p><code>zapf</code> currently takes a place, a function, and a list of arguments. What if we
made it more like <code>setf</code>, and just have it take a place and an expression? We
could still use <code>%</code> as a placeholder, but could let it appear anywhere in the
(possibly nested) expression form.</p>
<p>One way to do this would be to simply replace <code>substitute</code> with <code>subst</code> in the
<code>zapf</code> code. (The difference between these extremely-confusingly-named
functions is that <code>substitute</code> replaces element of a sequence, and <code>subst</code>
replaces elements of a tree.)</p>
<p>This, however, is the wrong strategy. Blindly replacing <code>%</code> everywhere in the
expression will work for many cases, but will break in edge cases like (god help
you) nested <code>zapf</code> forms. We could try to walk the code of the expression we
get, but down this path lies madness and implementation-specificity.</p>
<p>The solution is much, much simpler. We can just use a normal <code>let</code> to capture
<code>%</code> in the expression:</p>
<pre><code>; Final version
(defmacro <b>zapf</b> (<b>place expr</b>)
(multiple-value-bind
(temps exprs stores store-expr access-expr)
(get-setf-expansion place)
`(let* (,@(mapcar #'list temps exprs)
(,(car stores)
<b>(let ((% ,access-expr))
,expr)</b>))
,store-expr)))
</code></pre>
<p>And now <code>move-ball</code> only requires the bare minimum of typing:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> move-ball <span class="paren2">(<span class="code">ball distance screen-width</span>)</span>
<span class="paren2">(<span class="code">zapf <span class="paren3">(<span class="code">ball-x ball</span>)</span>
<span class="paren3">(<span class="code">mod <span class="paren4">(<span class="code">+ distance %</span>)</span> screen-width</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>For completeness it would be nice to allow <code>zapf</code> to take any number of
place/value pairs, just like <code>setf</code>. I'll leave this as an exercise for you to
try if you're interested.</p>
<p>It took me a while to arrive at this form of the macro. I personally find it
lovely and readable. If you disagree, that's fine too! Lisp is a wonderful
substrate for building a language that fits how you think and talk about
problems. Play around with your own syntax and find out what feels most natural
for you.</p>
<p>Before we leave, let's just ponder one more thing. Performance is often ignored
these days, but what if we care about making the computer go fast? The final
<code>zapf</code> macro is handy and expressive, but what cost have we paid for this
abstraction?</p>
<p>First let's tell SBCL that we want to go fast and return to our original <code>setf</code>
strategy:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">deftype</span></i> nonnegative-fixnum <span class="paren2">(<span class="code"></span>)</span>
`<span class="paren2">(<span class="code">integer 0 ,most-positive-fixnum</span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">deftype</span></i> positive-fixnum <span class="paren2">(<span class="code"></span>)</span>
`<span class="paren2">(<span class="code">integer 1 ,most-positive-fixnum</span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defstruct</span></i> ball
<span class="paren2">(<span class="code">x 0 <span class="keyword">:type</span> nonnegative-fixnum</span>)</span>
<span class="paren2">(<span class="code">y 0 <span class="keyword">:type</span> nonnegative-fixnum</span>)</span></span>)</span>
<span class="paren1">(<span class="code">declaim <span class="paren2">(<span class="code">ftype <span class="paren3">(<span class="code"><i><span class="symbol">function</span></i> <span class="paren4">(<span class="code">ball fixnum positive-fixnum</span>)</span></span>)</span>
move-ball</span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> move-ball <span class="paren2">(<span class="code">ball distance screen-width</span>)</span>
<span class="paren2">(<span class="code">declare <span class="paren3">(<span class="code">optimize <span class="paren4">(<span class="code">speed 3</span>)</span> <span class="paren4">(<span class="code">safety 0</span>)</span> <span class="paren4">(<span class="code">debug 0</span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">setf <span class="paren3">(<span class="code">ball-x ball</span>)</span>
<span class="paren3">(<span class="code">mod <span class="paren4">(<span class="code">+ distance <span class="paren5">(<span class="code">ball-x ball</span>)</span></span>)</span>
screen-width</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>How does that turn out?</p>
<pre><code>; disassembly for MOVE-BALL
; Size: 82 bytes. Origin: #x1005E5E12E
; 2E: 488B410D MOV RAX, [RCX+13] ; no-arg-parsing entry point
; 32: 48D1F8 SAR RAX, 1
; 35: 498D3C00 LEA RDI, [R8+RAX]
; 39: 488BDE MOV RBX, RSI
; 3C: 48D1FB SAR RBX, 1
; 3F: 4885DB TEST RBX, RBX
; 42: 7431 JEQ L3
; 44: 488BC7 MOV RAX, RDI
; 47: 4899 CQO
; 49: 48F7FB IDIV RAX, RBX
; 4C: 4C8BC0 MOV R8, RAX
; 4F: 488BC2 MOV RAX, RDX
; 52: 48D1E0 SHL RAX, 1
; 55: 4885C0 TEST RAX, RAX
; 58: 7510 JNE L2
; 5A: L0: 488BD8 MOV RBX, RAX
; 5D: L1: 4889590D MOV [RCX+13], RBX
; 61: 488BD3 MOV RDX, RBX
; 64: 488BE5 MOV RSP, RBP
; 67: F8 CLC
; 68: 5D POP RBP
; 69: C3 RET
; 6A: L2: 4885FF TEST RDI, RDI
; 6D: 7DEB JNL L0
; 6F: 488D1C30 LEA RBX, [RAX+RSI]
; 73: EBE8 JMP L1
; 75: L3: 0F0B0A BREAK 10 ; error trap
; 78: 07 BYTE #X07
; 79: 08 BYTE #X08 ; DIVISION-BY-ZERO-ERROR
; 7A: FE9E03 BYTE #XFE, #X9E, #X03 ; RDI
; 7D: FE9E01 BYTE #XFE, #X9E, #X01 ; RBX</code></pre>
<p>Well that's not <em>too</em> bad. I'm not sure why SBCL still performs a check for
division by zero for the <code>mod</code> even though we said that <code>screen-width</code> can't
possibly be zero. But hey, we're at a manageable amount of assembly, which is
pretty nice for a high-level language!</p>
<p>Now for the <code>zapf</code> version:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> move-ball <span class="paren2">(<span class="code">ball distance screen-width</span>)</span>
<span class="paren2">(<span class="code">declare <span class="paren3">(<span class="code">optimize <span class="paren4">(<span class="code">speed 3</span>)</span> <span class="paren4">(<span class="code">safety 0</span>)</span> <span class="paren4">(<span class="code">debug 0</span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code">zapf <span class="paren3">(<span class="code">ball-x ball</span>)</span>
<span class="paren3">(<span class="code">mod <span class="paren4">(<span class="code">+ distance %</span>)</span> screen-width</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>Okay, let's bite the bullet. How bad are we talking?</p>
<pre><code>; disassembly for MOVE-BALL
; Size: 70 bytes. Origin: #x1005F47C7B
; 7B: 488B410D MOV RAX, [RCX+13] ; no-arg-parsing entry point
; 7F: 48D1F8 SAR RAX, 1
; 82: 488D3C03 LEA RDI, [RBX+RAX]
; 86: 4885F6 TEST RSI, RSI
; 89: 742B JEQ L2
; 8B: 488BC7 MOV RAX, RDI
; 8E: 4899 CQO
; 90: 48F7FE IDIV RAX, RSI
; 93: 488BD8 MOV RBX, RAX
; 96: 488D0412 LEA RAX, [RDX+RDX]
; 9A: 4885C0 TEST RAX, RAX
; 9D: 750D JNE L1
; 9F: L0: 48D1E2 SHL RDX, 1
; A2: 4889510D MOV [RCX+13], RDX
; A6: 488BE5 MOV RSP, RBP
; A9: F8 CLC
; AA: 5D POP RBP
; AB: C3 RET
; AC: L1: 4885FF TEST RDI, RDI
; AF: 7DEE JNL L0
; B1: 4801F2 ADD RDX, RSI
; B4: EBE9 JMP L0
; B6: L2: 0F0B0A BREAK 10 ; error trap
; B9: 07 BYTE #X07
; BA: 08 BYTE #X08 ; DIVISION-BY-ZERO-ERROR
; BB: FE9E03 BYTE #XFE, #X9E, #X03 ; RDI
; BE: FE1E03 BYTE #XFE, #X1E, #X03 ; RSI</code></pre>
<p>Wait, it's actually <em>shorter</em>? What about all those extra variables the <code>zapf</code>
macro expands into? It turns out that SBCL is really quite good at optimizing
<code>let</code> statements and local variables.</p>
<p>This is why I love Common Lisp. You can be rewriting the syntax of the language
and working on a tower of abstraction one moment, and looking at X86 assembly to
see what the hell the computer is actually <em>doing</em> the next. It's wonderful.</p>
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a><a href='https://twitter.com/stevelosh/'>Twitter</a><a href='https://instagram.com/thirtytwobirds/'>Instagram</a><a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html>