452 lines
No EOL
30 KiB
HTML
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 "parallel <code>setf</code>". 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">; => (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">; => (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 "place" 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 "in-place" 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 "set <code>place</code> to <code>1/place</code>":</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">&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">; => 5
|
|
</span> <span class="paren2">(<span class="code">divf x</span>)</span> <span class="comment">; => 1/5
|
|
</span> <span class="paren2">(<span class="code">divf x</span>)</span></span>)</span> <span class="comment">; => 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">; => 1
|
|
</span> <span class="paren2">(<span class="code">callf x #'1+</span>)</span> <span class="comment">; => 2
|
|
</span> <span class="paren2">(<span class="code">callf x #'sin</span>)</span> <span class="comment">; => 0.9092974
|
|
</span> <span class="paren2">(<span class="code">callf x #'round</span>)</span> <span class="comment">; => 1
|
|
</span> <span class="paren2">(<span class="code">callf x #'-</span>)</span></span>)</span> <span class="comment">; => -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>&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> &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> &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">; => 1
|
|
</span> <span class="paren2">(<span class="code">callf x #'+ 10</span>)</span> <span class="comment">; => 11
|
|
</span> <span class="paren2">(<span class="code">callf x #'* 2 4</span>)</span></span>)</span> <span class="comment">; => 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">; => (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">; => (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">; => (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 &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> &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">; => (:coffee :tea)
|
|
</span>
|
|
<span class="paren2">(<span class="code">zapf x #'cons <span class="keyword">:water</span> %</span>)</span>
|
|
<span class="comment">; => (: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">; => (: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> |