<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2209407496177464044</id><updated>2011-11-27T21:53:06.204-05:00</updated><category term='slot access'/><category term='websites'/><category term='build'/><category term='comparison'/><category term='books'/><category term='runtime'/><category term='macros'/><category term='automatic'/><category term='restarts'/><category term='lisp'/><category term='speed test'/><category term='conference'/><category term='academic'/><category term='Scheme'/><category term='teaching'/><category term='code generation'/><title type='text'>Exploring Lisp</title><subtitle type='html'>Writings on Lisp and the process of programming.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>42</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-7658144586831226912</id><published>2009-11-15T13:07:00.001-05:00</published><updated>2009-11-15T13:08:17.508-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='build'/><title type='text'>Initial thoughts on XCVB</title><content type='html'>&lt;p&gt;Having struggled with the &lt;a href="http://maven.apache.org/"&gt;build system at work&lt;/a&gt; lately, I have decided to force myself to learn another build system at home: I am using &lt;a href="http://common-lisp.net/project/xcvb/"&gt;XCVB&lt;/a&gt; for my UNIVAC emulator project.&lt;/p&gt;

&lt;p&gt;I was intrigued by XCVB when I read about it in ILC'09.  ASDF has always felt unfinished, even though it's a vast improvement over loading files manually (or using customized system definitions).  However, I've run into problems with ASDF and I'm not against other options.&lt;/p&gt;

&lt;p&gt;Some thoughts I had during the building, installation and use of XCVB:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The name needs to change.  Is it possible to pick a name that is not some strained acronym?  Ant. Maven. Make.  These are reasonable (if not goofy) names.  XCVB (and for that matter, ASDF) is just alphabet soup.  Pick a word and go with it.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://www.cliki.net/cl-launch"&gt;cl-launch&lt;/a&gt; feels like a whole lot of magic, but I've seen a whole lot of other magic work before.&lt;/li&gt;
  &lt;li&gt;The process of building your own systems is a bit awkward right now.  &lt;code&gt;xcvb make-makefile --build foo&lt;/code&gt; when in the directory with the build file for &lt;code&gt;foo&lt;/code&gt; is excessive specification.  Building the system is even more awkward: &lt;code&gt;make -f xcvb.mk obj/foo.image&lt;/code&gt;.  Something akin to &lt;code&gt;make foo&lt;/code&gt; would be much nicer.&lt;/li&gt;
  &lt;li&gt;The idea of introducing metadata into a source file to indicate its dependencies &lt;a href="http://www.mail-archive.com/asdf-devel@common-lisp.net/msg00328.html"&gt;doesn't sit well with some people&lt;/a&gt;.  Personally, I like information about dependencies being close at hand; whether it comes from textual information or system queries doesn't matter.  I'm content with textual information at the level of a file for now, but I suspect maintaining that information will become a pain.  On the other hand, I found maintaining ASDF system definition files to be a pain (especially with refactoring and reorganization).  As much as I would prefer to not use files in a Lisp system, files are a reality.  I think starting with file-level meta-information is a good place to start in order find out what works and what doesn't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hopefully I get some time to try and help out with the project since I think it's ambitious enough to work.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-7658144586831226912?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/7658144586831226912/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=7658144586831226912' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7658144586831226912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7658144586831226912'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/11/initial-thoughts-on-xcvb.html' title='Initial thoughts on XCVB'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-1652915318119931133</id><published>2009-07-19T15:19:00.001-05:00</published><updated>2009-07-19T15:19:55.194-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='speed test'/><category scheme='http://www.blogger.com/atom/ns#' term='macros'/><title type='text'>Postscript to the jump-tables stuff</title><content type='html'>&lt;p&gt;As some commenters pointed out in my &lt;a href="http://exploring-lisp.blogspot.com/2009/07/jump-tables-follow-up.html"&gt;last jump tables post&lt;/a&gt;, my recommendation to not really worry about jump tables misses uses cases related to compiler and macro writers.  It may be there are cases where hundreds (or even thousands) of cases will appear in a single conditional.&lt;/p&gt;

&lt;p&gt;I should have been more clear in what I was trying to say: If you're writing &lt;code&gt;case&lt;/code&gt; expressions directly (not generated), then you probably never have to worry about jump tables.  That is, if you think you should take your &lt;code&gt;case&lt;/code&gt; expression and write a jump table macro for it, you're probably wasting your time.  If you are generating &lt;code&gt;case&lt;/code&gt; expressions, you may have to consider the possibility when the number of cases is very large since the data indicates hundreds or thousands of cases does make a difference.&lt;/p&gt;

&lt;p&gt;Of course, these measurements assumed that the &lt;code&gt;case&lt;/code&gt; expression used elements such that a jump table was even reasonably representable (see &lt;a href="http://exploring-lisp.blogspot.com/2009/07/tinkering-with-jump-tables.html?showComment=1247508462244#c8833753493331411509"&gt;Duane Rettig's comment&lt;/a&gt;).  It's a relatively narrow use case, but is likely applicable to most other situations where the number of conditions is hovering around ten.&lt;/p&gt;

&lt;p&gt;Thanks to the those who commented.  It helps keep these things honest. :)  (Comments are the peer review of blogging, after all.)  Hopefully I'll get around to looking at some other aspects of this work soon.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-1652915318119931133?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/1652915318119931133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=1652915318119931133' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1652915318119931133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1652915318119931133'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/07/postscript-to-jump-tables-stuff.html' title='Postscript to the jump-tables stuff'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5579872605418763534</id><published>2009-07-16T11:13:00.001-05:00</published><updated>2009-07-16T11:14:14.374-05:00</updated><title type='text'>SMP in Allegro CL 9.0</title><content type='html'>&lt;p&gt;Franz's latest tech update states that &lt;a href="http://www.franz.com/support/tech_corner/#smp071409"&gt;SMP is coming to Allegro CL 9.0&lt;/a&gt; and recommends people work on migrating their code, since it may break things.&lt;/p&gt;

&lt;p&gt;With this news, I must say I'm looking forward to trying out ACL 9.0.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5579872605418763534?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5579872605418763534/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5579872605418763534' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5579872605418763534'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5579872605418763534'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/07/smp-in-franz.html' title='SMP in Allegro CL 9.0'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-7111831991299718042</id><published>2009-07-16T11:05:00.001-05:00</published><updated>2009-07-16T11:05:44.690-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='speed test'/><category scheme='http://www.blogger.com/atom/ns#' term='macros'/><title type='text'>Jump tables follow-up</title><content type='html'>&lt;p&gt;Based on comments from &lt;a href="http://exploring-lisp.blogspot.com/2009/07/tinkering-with-jump-tables.html"&gt;my previous post&lt;/a&gt; on jump tables, I decided to follow-up with a slightly more comprehensive analysis.&lt;/p&gt;

&lt;p&gt;Below is the running time, in milliseconds, to execute a given &lt;code&gt;case&lt;/code&gt;-style macro one million times with &lt;em&gt;n&lt;/em&gt; branches; each execution resulted in the code taking the default case.  These running times include the changes suggested by &lt;a href="http://jsnell.iki.fi/blog/"&gt;Juho Snellman&lt;/a&gt; (seconded by &lt;a href="http://cl-user.net/asp/yEaG/sdataQ1Ev-NWPMe8hDQ3PNR8X8yBX8yBXnMq=/sdataQu3F$sSHnB=="&gt;Duane Rettig&lt;/a&gt;) to make the times somewhat more accurate, that is, use &lt;code&gt;funcall&lt;/code&gt; on a function object instead of a symbol.&lt;/p&gt;

&lt;p&gt;At the request of &lt;a href="http://www.foldr.org/~michaelw/"&gt;Michael Weber&lt;/a&gt;, I included &lt;a href="http://www.foldr.org/~michaelw/log/programming/lisp/icfp-contest-2006-vm"&gt;his &lt;code&gt;ecase&lt;/code&gt; macro&lt;/a&gt;, except that has been renamed to &lt;code&gt;case/tree&lt;/code&gt; and changed slightly to accept a default case.&lt;/p&gt;

&lt;p&gt;A '&amp;mdash;' means that I could not get the given use case to compile.&lt;/p&gt;

&lt;table style="border-collapse: separate; border-spacing: 1em 0px;"&gt;
  &lt;tr&gt;
    &lt;th colspan="6" style="padding-bottom: 1em;"&gt;Jump table running times (ms)&lt;/th&gt;
  &lt;/tr&gt;

  &lt;tr&gt;
    &lt;th colspan="2" align="left"&gt;Clozure Common Lisp 1.3-RC1&lt;/th&gt;
    &lt;th&gt;case&lt;/th&gt;
    &lt;th&gt;ncase&lt;/th&gt;
    &lt;th&gt;switch&lt;/th&gt;
    &lt;th&gt;case/tree&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;th rowspan="3" align="center"&gt;n&lt;/th&gt;
    &lt;td align="left"&gt;10&lt;/td&gt;
    &lt;td&gt;45&lt;/td&gt;
    &lt;td&gt;52&lt;/td&gt;
    &lt;td&gt;361&lt;/td&gt;
    &lt;td&gt;42&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;100&lt;/td&gt;
    &lt;td&gt;278&lt;/td&gt;
    &lt;td&gt;51&lt;/td&gt;
    &lt;td&gt;443&lt;/td&gt;
    &lt;td&gt;68&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;1000&lt;/td&gt;
    &lt;td&gt;3447&lt;/td&gt;
    &lt;td&gt;51&lt;/td&gt;
    &lt;td&gt;725&lt;/td&gt;
    &lt;td&gt;88&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr align="right"&gt;
    &lt;th colspan="2" align="left"&gt;SCBL 1.0.29.54.rc2&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;th rowspan="3" align="center"&gt;n&lt;/th&gt;
    &lt;td align="left"&gt;10&lt;/td&gt;
    &lt;td&gt;54&lt;/td&gt;
    &lt;td&gt;60&lt;/td&gt;
    &lt;td&gt;222&lt;/td&gt;
    &lt;td&gt;63&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;100&lt;/td&gt;
    &lt;td&gt;218&lt;/td&gt;
    &lt;td&gt;59&lt;/td&gt;
    &lt;td&gt;203&lt;/td&gt;
    &lt;td&gt;88&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;1000&lt;/td&gt;
    &lt;td&gt;2029&lt;/td&gt;
    &lt;td&gt;60&lt;/td&gt;
    &lt;td&gt;222&lt;/td&gt;
    &lt;td&gt;112&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr align="right"&gt;
    &lt;th colspan="2" align="left"&gt;ACL (free edition) 8.1&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;th rowspan="3" align="center"&gt;n&lt;/th&gt;
    &lt;td align="left"&gt;10&lt;/td&gt;
    &lt;td&gt;40&lt;/td&gt;
    &lt;td&gt;50&lt;/td&gt;
    &lt;td&gt;280&lt;/td&gt;
    &lt;td&gt;50&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;100&lt;/td&gt;
    &lt;td&gt;40&lt;/td&gt;
    &lt;td&gt;50&lt;/td&gt;
    &lt;td&gt;270&lt;/td&gt;
    &lt;td&gt;50&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;1000&lt;/td&gt;
    &lt;td&gt;4160&lt;/td&gt;
    &lt;td&gt;40&lt;/td&gt;
    &lt;td&gt;340&lt;/td&gt;
    &lt;td&gt;70&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr align="right"&gt;
    &lt;th colspan="2" align="left"&gt;Lispworks Personal 5.1.1&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;th rowspan="3" align="center"&gt;n&lt;/th&gt;
    &lt;td align="left"&gt;10&lt;/td&gt;
    &lt;td&gt;43&lt;/td&gt;
    &lt;td&gt;44&lt;/td&gt;
    &lt;td&gt;288&lt;/td&gt;
    &lt;td&gt;48&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;100&lt;/td&gt;
    &lt;td&gt;258&lt;/td&gt;
    &lt;td&gt;43&lt;/td&gt;
    &lt;td&gt;237&lt;/td&gt;
    &lt;td&gt;54&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;1000&lt;/td&gt;
    &lt;td&gt;3064&lt;/td&gt;
    &lt;td&gt;&amp;mdash;&lt;/td&gt;
    &lt;td&gt;277&lt;/td&gt;
    &lt;td&gt;69&lt;/td&gt;
  &lt;/tr&gt;

  &lt;tr align="right"&gt;
    &lt;th colspan="2" align="left"&gt;CLISP 2.47&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;th rowspan="3" align="center"&gt;n&lt;/th&gt;
    &lt;td align="left"&gt;10&lt;/td&gt;
    &lt;td&gt;363&lt;/td&gt;
    &lt;td&gt;550&lt;/td&gt;
    &lt;td&gt;615&lt;/td&gt;
    &lt;td&gt;804&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;100&lt;/td&gt;
    &lt;td&gt;390&lt;/td&gt;
    &lt;td&gt;549&lt;/td&gt;
    &lt;td&gt;693&lt;/td&gt;
    &lt;td&gt;1332&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr align="right"&gt;
    &lt;td align="left"&gt;1000&lt;/td&gt;
    &lt;td&gt;368&lt;/td&gt;
    &lt;td&gt;549&lt;/td&gt;
    &lt;td&gt;612&lt;/td&gt;
    &lt;td&gt;1898&lt;/td&gt;
  &lt;/tr&gt;

&lt;/table&gt;

&lt;p&gt;I should point out that you shouldn&amp;#x27;t put much stock into the &lt;em&gt;n = 1000&lt;/em&gt; case since any &lt;code&gt;case&lt;/code&gt; macro that uses 1000 or more cases is almost certainly pathological.  Duane made this point in the comments of my previous post and I think it&amp;#x27;s important to consider.  It might be tempting to say that CLISP has a &amp;quot;better&amp;quot; &lt;code&gt;case&lt;/code&gt; macro since it has the same performance on 10 elements as it does for 1000; however, this would miss the point that most other Lisp systems are about 6-9 times faster in the vast majority of cases.  Even &lt;em&gt;n = 100&lt;/em&gt; is probably so rare that you&amp;#x27;d never see it in daily work unless you went digging.&lt;/p&gt;

&lt;p&gt;Below is a bar graph of the performance of &lt;code&gt;ncase&lt;/code&gt;, &lt;code&gt;switch&lt;/code&gt; and &lt;code&gt;case/tree&lt;/code&gt; relative to &lt;code&gt;case&lt;/code&gt;.  Each bar represents the relative difference for the performance of a given macro on &lt;em&gt;n&lt;/em&gt; branches compared to &lt;code&gt;case&lt;/code&gt;; I omitted the case of &lt;em&gt;n = 1000&lt;/em&gt; since I can't see it being practical.  A value of 0 indicates it is equivalent to &lt;code&gt;case&lt;/code&gt;.  For example, in Lispworks, the &lt;code&gt;ncase&lt;/code&gt; macro is 5 times faster with 100 branches than case (represented by the dark blue colour &lt;span style="color: #3300FF;"&gt;&amp;#x2588;&lt;/span&gt; bar).  Conversely, in Clozure CL, the switch macro is about 7 times slower with 10 branches than the case macro (the red colour &lt;span style="color: #FF0000;"&gt;&amp;#x2588;&lt;/span&gt; bar).&lt;/p&gt;

&lt;div style="text-align:center;"&gt;
  &lt;a href="http://lh3.ggpht.com/_StomEfmr9Cc/Sl9I_hRRDBI/AAAAAAAAADk/GkzwEFcNjtI/jump-tables.png?imgmax=800"&gt;&lt;img src="http://lh3.ggpht.com/_StomEfmr9Cc/Sl9I_hRRDBI/AAAAAAAAADk/GkzwEFcNjtI/jump-tables.png?imgmax=800" alt="jump-tables.png" border="0" width="50%" /&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;p&gt;What is clear from the graphs is that &lt;code&gt;switch&lt;/code&gt; doesn't provide any benefits over &lt;code&gt;case&lt;/code&gt; (when the cases are all single integer values, anyway).  Also, using &lt;code&gt;ncase&lt;/code&gt; or &lt;code&gt;case/tree&lt;/code&gt; is of some benefit when the number of cases is large, except when using Allegro CL or CLISP.  Probably the most interesting aspect is that &lt;code&gt;ncase&lt;/code&gt; and &lt;code&gt;case/tree&lt;/code&gt; tend to be slightly worse for small numbers of branches.&lt;/p&gt;

&lt;p&gt;There are still some interesting questions that could be explored here (Can a compelling use case for &lt;code&gt;switch&lt;/code&gt; be made? How often do large branch &lt;code&gt;case&lt;/code&gt; expressions appear in actual code?), but it seems to me that jump table macros are, for the most part, of academic interest only.  You can probably feel pretty confident that using &lt;code&gt;case&lt;/code&gt; is sufficient.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-7111831991299718042?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/7111831991299718042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=7111831991299718042' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7111831991299718042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7111831991299718042'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/07/jump-tables-follow-up.html' title='Jump tables follow-up'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_StomEfmr9Cc/Sl9I_hRRDBI/AAAAAAAAADk/GkzwEFcNjtI/s72-c/jump-tables.png?imgmax=800' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-4774288296022175848</id><published>2009-07-12T19:11:00.004-05:00</published><updated>2009-07-12T19:19:58.490-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='speed test'/><category scheme='http://www.blogger.com/atom/ns#' term='macros'/><title type='text'>Tinkering with jump tables</title><content type='html'>&lt;p&gt;I've often wondered whether writing a &lt;code&gt;case&lt;/code&gt; or &lt;code&gt;cond&lt;/code&gt;-like macro that uses jump tables is worth the effort.  To get an idea as to how much I should pursue this, I decided to take &lt;a href="http://www.tfeb.org/lisp/toys.html#NCASE"&gt;Tim Bradshaw's &lt;code&gt;ncase&lt;/code&gt; macro&lt;/a&gt; and pit it against &lt;code&gt;case&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tim admits that &lt;code&gt;ncase&lt;/code&gt; is meant to show what is possible with macros and that it may be the case it may not make any performance improvement at all.  Still, I think it would be interesting to compare it and see the results, simply because I really don't know what will happen.&lt;/p&gt;

&lt;p&gt;In addition to Tim's jump table, I wrote a simple jump table implementation called &lt;code&gt;switch&lt;/code&gt; that uses a hash table to look up the value and associates the body with a thunk; the hash table is computed with an &lt;code&gt;load-time-value&lt;/code&gt; to minimize table creation overhead.  &lt;code&gt;switch&lt;/code&gt; has the disadvantage of performing a hash table lookup and calling a function with the advantage that the values to match can be anything you would compare with &lt;code&gt;equal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To compare the running times, I generated functions that contain 10, 100 and 1000 branches respectively (plus the default branch).  This was done for each of &lt;code&gt;case&lt;/code&gt;, &lt;code&gt;ncase&lt;/code&gt; and &lt;code&gt;switch&lt;/code&gt;.  Each branch would compare a single integer and return that integer.  For example, the body of &lt;code&gt;case-100&lt;/code&gt; would look something like the following.&lt;/p&gt;

&lt;code&gt;
  (case v (1 1) (2 2) ... (100 100) (t nil))
&lt;/code&gt;

&lt;p&gt;It's easy to see why I generated these function bodies with macros...&lt;/p&gt;

&lt;p&gt;I ran each function one million times, making it hit the default case each time.  The time required to run the function one million times on different Lisp systems are given below in milliseconds.  These were run on a 2.16 GHZ 17" Macbook Pro running Mac OS X 10.5.7.&lt;/p&gt;

&lt;table border="0"&gt;
  &lt;tr&gt;&lt;td&gt;SBCL 1.0.29.54.rc2&lt;/td&gt; &lt;td&gt;&lt;code&gt;case&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code&gt;ncase&lt;/code&gt;&lt;/td&gt;  &lt;td&gt;&lt;code&gt;switch&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;10&lt;/td&gt; &lt;td&gt;469&lt;/td&gt; &lt;td&gt;469&lt;/td&gt; &lt;td&gt;633&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;100&lt;/td&gt; &lt;td&gt;631&lt;/td&gt; &lt;td&gt;471&lt;/td&gt; &lt;td&gt;612&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;1000&lt;/td&gt; &lt;td&gt;2455&lt;/td&gt; &lt;td&gt;469&lt;/td&gt; &lt;td&gt;630&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;CLISP 2.47&lt;/td&gt; &lt;td&gt;&lt;code&gt;case&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code&gt;ncase&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code&gt;switch&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;10&lt;/td&gt; &lt;td&gt;348&lt;/td&gt; &lt;td&gt;558&lt;/td&gt; &lt;td&gt;579&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;100&lt;/td&gt; &lt;td&gt;391&lt;/td&gt; &lt;td&gt;561&lt;/td&gt; &lt;td&gt;645&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;1000&lt;/td&gt; &lt;td&gt;375&lt;/td&gt; &lt;td&gt;560&lt;/td&gt; &lt;td&gt;571&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;ACL Free edition 8.1&lt;/td&gt; &lt;td&gt;&lt;code&gt;case&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code&gt;ncase&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code&gt;switch&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;10&lt;/td&gt; &lt;td&gt;40&lt;/td&gt; &lt;td&gt;40&lt;/td&gt; &lt;td&gt;280&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;100&lt;/td&gt; &lt;td&gt;40&lt;/td&gt; &lt;td&gt;50&lt;/td&gt; &lt;td&gt;270&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;1000&lt;/td&gt; &lt;td&gt;4290&lt;/td&gt; &lt;td&gt;50&lt;/td&gt; &lt;td&gt;350&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;I was quite surprised at the results.  I ran these a few times to make sure I wasn't missing something.  In particular, I'm drawn to the results from CLISP.  It seems lookups with case is constant.  The reason this is so compelling is that SBCL and ACL are quite slow in these cases.  ACL seems to be very fast otherwise.&lt;/p&gt;

&lt;p&gt;I'll have to take a closer look at the disassembled code to see what is going on (or perhaps you can mention something in the comments if you know).  Regardless, it's something that deserves some more attention.&lt;/p&gt;

&lt;p&gt;Some notes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SBCL took approximately 4 minutes to compile the &lt;code&gt;ncase&lt;/code&gt; with 1000 branches.  It also used up an awful lot of memory in the process.  It took more than 10 seconds to compile the &lt;code&gt;switch&lt;/code&gt; with 1000 cases.&lt;/li&gt;
  &lt;li&gt;The code I used to get these numbers can be found on &lt;a href="http://paste.lisp.org/display/83487"&gt;paste.lisp.org&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-4774288296022175848?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/4774288296022175848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=4774288296022175848' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4774288296022175848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4774288296022175848'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/07/tinkering-with-jump-tables.html' title='Tinkering with jump tables'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-9056322126008430506</id><published>2009-07-12T17:35:00.001-05:00</published><updated>2009-07-12T17:35:14.464-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='websites'/><title type='text'>Franz's facelift</title><content type='html'>&lt;p&gt;I guess it's been a while since I visited &lt;a href="http://franz.com/"&gt;Franz's website&lt;/a&gt;.  It appears to have gotten a serious makeover.  Last I checked, it &lt;a href="http://web.archive.org/web/20070217231248/http://www.franz.com/"&gt;looked very 1990s-ish&lt;/a&gt;.  It looks much better now.&lt;/p&gt;

&lt;p&gt;I'd rank it about as effective as &lt;a href="http://www.sbcl.org/"&gt;SBCL's simplicity&lt;/a&gt; and considerably better than the cluttered &lt;a href="http://clisp.cons.org/"&gt;CLISP bucket 'o links&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-9056322126008430506?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/9056322126008430506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=9056322126008430506' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/9056322126008430506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/9056322126008430506'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/07/franz-facelift.html' title='Franz&amp;#39;s facelift'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-4954115528165295659</id><published>2009-03-25T17:40:00.001-05:00</published><updated>2009-03-25T17:40:40.947-05:00</updated><title type='text'>Thanks to the ILC 2009 Twitterers</title><content type='html'>&lt;p&gt;Just wanted to say thanks to all those who posted updates about ILC 2009 on Twitter.  It made me feel bad for having to miss the conference, but at least I got to keep up on what was going on.&lt;/p&gt;

&lt;p&gt;Now, anyone up for writing a blog entry providing a little more detail?&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-4954115528165295659?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/4954115528165295659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=4954115528165295659' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4954115528165295659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4954115528165295659'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/03/thanks-to-ilc-2009-twitterers.html' title='Thanks to the ILC 2009 Twitterers'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-53867573045194037</id><published>2009-03-23T05:35:00.001-05:00</published><updated>2009-03-23T05:35:20.188-05:00</updated><title type='text'>Following ILC 2009 on Twitter</title><content type='html'>Like &lt;a href="http://xach.livejournal.com/214367.html"&gt;Zach&lt;/a&gt; said, you can follow the updates tagged with &lt;a href="http://search.twitter.com/search?q=%23ilc2009"&gt;#ilc2009&lt;/a&gt;.

With what &lt;a href="http://twitter.com/danbernier"&gt;danbernier&lt;/a&gt; is posting, I'm wondering if it's the International Clojure Conference...

I'm curious to see what today brings now that the tutorials are over.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-53867573045194037?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/53867573045194037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=53867573045194037' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/53867573045194037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/53867573045194037'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/03/following-ilc-2009-on-twitter.html' title='Following ILC 2009 on Twitter'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-108129063027769776</id><published>2009-03-23T05:31:00.001-05:00</published><updated>2009-03-23T05:31:01.083-05:00</updated><title type='text'>CADR Emulator on Leopard</title><content type='html'>&lt;p&gt;I managed to get the CADR Emulator up and running on OS X 10.5; the current distribution only supports 10.4. The changes were minimal, but I'm still testing it out to make sure nothing is seriously wrong.&lt;/p&gt;

One thing is problematic: it doesn't like the year 2009.

&lt;div style="text-align:center;"&gt;&lt;img src="http://lh5.ggpht.com/_StomEfmr9Cc/ScdkawN2ToI/AAAAAAAAADA/X-_3hWSSoPA/CADR%20Screenshot.png?imgmax=800" alt="Screenshot of CADR emulator thinking the year is 19109" border="0" width="562" height="112" /&gt;&lt;/div&gt;

I tried entering 1990 once, but it resulted in my machine locking up. I'm pretty sure it was the emulator that locked up my machine, but I don't know if the date had anything to do with it.

(Since I'm not at ILC 2009 &amp;mdash; &lt;em&gt;sigh&lt;/em&gt; &amp;mdash; I figured I should at least do something Lisp-y.)
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-108129063027769776?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/108129063027769776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=108129063027769776' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/108129063027769776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/108129063027769776'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/03/cadr-emulator-on-leopard.html' title='CADR Emulator on Leopard'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_StomEfmr9Cc/ScdkawN2ToI/AAAAAAAAADA/X-_3hWSSoPA/s72-c/CADR%20Screenshot.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-6071169536993509528</id><published>2009-03-19T11:19:00.001-05:00</published><updated>2009-03-19T11:19:29.264-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><title type='text'>Unfortunate circumstances</title><content type='html'>&lt;p&gt;I've recently come down with a serious eye infection and as a result, I will have to cancel my trip to &lt;a href="http://www.international-lisp-conference.org/2009/index"&gt;ILC 2009&lt;/a&gt;. I'm really disappointed with this development, but I've decided to value my health over that of the conference.&lt;/p&gt;

&lt;p&gt;The infection is in conjunction with a condition called &lt;a href="http://www.stlukeseye.com/Conditions/CornealErosion.asp"&gt;recurrent corneal erosion&lt;/a&gt;. The section of eroded cornea recently expanded and managed to become infected. The pain I experienced was severe and intense. That pain has since subsided, but my vision is blurry and I have to administer eye drops frequently. I decided that travelling in this condition wouldn't be wise, especially if it were to flare up again.&lt;/p&gt;

&lt;p&gt;I really didn't want to have to make this choice and delayed it for as long as possible. I had lots of discussions planned and was working on a couple lightning talks, but that's not going to happen now.&lt;/p&gt;

&lt;p&gt;I thought some people would want to know in the event someone wanted to talk to me (I know I was going to track a couple people down and chat with them!). Here's hoping the conference goes well. I'll try to stay active on &lt;a href="http://ilc2009.scheming.org/forum"&gt;the forums&lt;/a&gt; (even though using a computer is difficult right now) and will write some blog entries in lieu of lightning talks. I'll also try to finish my UNIVAC I emulator and get it up on the web soon.&lt;/p&gt;

&lt;p&gt;Here's hoping there's lots of blogging about the conference -- and that I don't have to wait two years for another ILC! :)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-6071169536993509528?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/6071169536993509528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=6071169536993509528' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/6071169536993509528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/6071169536993509528'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/03/unfortunate-circumstances.html' title='Unfortunate circumstances'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-938399541082922580</id><published>2009-03-01T01:12:00.001-05:00</published><updated>2009-03-01T01:12:03.275-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='conference'/><title type='text'>Heading to ILC 2009</title><content type='html'>&lt;p&gt;To get myself back into a Lisp frame of mind, I'm going to &lt;a href="http://www.international-lisp-conference.org/2009/index"&gt;ILC 2009&lt;/a&gt;.  I may also sign up to give a couple lightning talks since there seems to be so much &lt;a href="http://www.international-lisp-conference.org/2009/schedule"&gt;space available&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My current employment involves embedded programming in Java and takes up most of my days.  Still, I've managed to keep doing stuff with Lisp in my (short) spare time.&lt;/p&gt;

&lt;p&gt;One Lisp project I'm working on that is a candidate for a lightning talk is a &lt;a href="http://en.wikipedia.org/wiki/UNIVAC_I"&gt;UNIVAC I&lt;/a&gt; emulator.  Part of it is just for the fun of implementing an emulator, but also to experiment with self-adjusting systems.&lt;/p&gt;

&lt;p&gt;Another project I may talk about is some stats I'm trying to collect regarding coding conventions.  I think refactoring for Lisp might be well served by taking advantage of conventions.  My work has consisted of basic code analysis, looking for commonalities across various projects.&lt;/p&gt;

&lt;p&gt;Neither of these are terribly deep projects at this point, making them good candidates for lightning talks.&lt;/p&gt;

&lt;p&gt;As for the schedule at ILC'09, there seems to be some interesting stuff.  I'm wondering how much conversation will revolve around Clojure and not (Common) Lisp.  I hope there is some discussion around some big, "pie-in-the-sky" topics in addition to the usual pragmatism.&lt;/p&gt;

&lt;p&gt;If anyone feels like chatting about such things, feel free to track me down.  I plan on attending as much as possible, even though Monday's schedule goes until 10 p.m. (!)&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-938399541082922580?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/938399541082922580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=938399541082922580' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/938399541082922580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/938399541082922580'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2009/03/heading-to-ilc-2009.html' title='Heading to ILC 2009'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-3591938504619186450</id><published>2008-10-24T05:58:00.001-05:00</published><updated>2008-10-24T05:58:48.659-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><title type='text'>Quick thought on dynamism</title><content type='html'>A recent &lt;a href="http://lukego.livejournal.com/18161.html"&gt;post by Luke Gorrie&lt;/a&gt; got me thinking about why dynamism is useful.  Having been a (paid) Java programmer for a little over three months now, I've come to appreciate a little more the static properties it offers, but grow increasingly frustrated by them.  The dichotomy breaks down as follows: I appreciate the static elements for what is already built, but find them frustrating while building.

Dynamism is a wonderful "genre" to work in and static is a good one to rely on.  I think the interesting questions lie in the transition between the two.
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-3591938504619186450?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/3591938504619186450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=3591938504619186450' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3591938504619186450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3591938504619186450'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/10/quick-thought-on-dynamism.html' title='Quick thought on dynamism'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-2071317629186645077</id><published>2008-09-02T05:49:00.001-05:00</published><updated>2008-09-02T05:49:31.832-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Slowly working through LiSP</title><content type='html'>&lt;p&gt;As of Friday at around 11:30 a.m., I officially have the degree of &lt;a href="http://en.wikipedia.org/wiki/Doctor_of_philosophy"&gt;Doctor of Philosophy&lt;/a&gt;.  To celebrate the end of an era for me, I spent the weekend relaxing with my wife at the lake.  Oh yes, and I worked through the first chapter of &lt;a href="http://pagesperso-systeme.lip6.fr/Christian.Queinnec/WWW/LiSP.html"&gt;Queinnec's book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just for fun, I'm translating as much of the code to Common Lisp as I can, although continuations could prove challenging.  Since it's a spare time project and I don't have the guilt of having to finish a thesis hanging over me, I don't mind.  If the Scheme to CL translation doesn't work out, I'll at least have to port some old Scheme code to new Scheme code.&lt;/p&gt;

&lt;p&gt;I should get around to posting the thesis in HTML format later this week, for those interested&lt;sup&gt;*&lt;/sup&gt;.  At the very least, a PDF version will make an appearance.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;*&lt;/sup&gt;Amazingly, a couple people have said they want to read it!  One of my colleagues said recently that a good thesis should collect dust (since it is the point from which new research starts), meaning no one should read it.  Here's hoping that violating this maxim doesn't mean the thesis is worthless.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-2071317629186645077?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/2071317629186645077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=2071317629186645077' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/2071317629186645077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/2071317629186645077'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/09/slowly-working-through-lisp.html' title='Slowly working through LiSP'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-4341144440293383910</id><published>2008-08-22T21:22:00.001-05:00</published><updated>2008-08-22T21:22:42.574-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Queinnec's book, at last!</title><content type='html'>&lt;p&gt;As a gift for successful completion of my thesis, a friend of mine gave me a copy of Queinnec's book, &lt;a href="http://pagesperso-systeme.lip6.fr/Christian.Queinnec/WWW/LiSP.html"&gt;Lisp In Small Pieces&lt;/a&gt;.  If I had this book earlier, I probably would have finished my thesis a lot later.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-4341144440293383910?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/4341144440293383910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=4341144440293383910' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4341144440293383910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4341144440293383910'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/08/queinnec-book-at-last.html' title='Queinnec&amp;#39;s book, at last!'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5297067955878027454</id><published>2008-08-21T23:47:00.001-05:00</published><updated>2008-08-21T23:47:03.777-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='academic'/><title type='text'>Behavioural synthesis, the (Lisp) thesis</title><content type='html'>&lt;p&gt;On August 18, I successfully defended my PhD thesis entitled &lt;em&gt;Structuring data via behavioural synthesis&lt;/em&gt;.  I explored the idea of deriving data structure for objects in a system by examining the behavioural aspects of a system.  I did all the programming for the work in Common Lisp and did three things in the thesis to demonstrate how behavioural synthesis can work.&lt;/p&gt;

&lt;p&gt;The first was a framework for defining data types whose instances can change their internal structure to accommodate different conditions related to their usage.  This is done at the individual object level.  It allows for highly adaptive objects, but incurs significant overhead.  This demonstrated using limited context to control structural changes.&lt;/p&gt;

&lt;p&gt;The second approach used this framework to expand the contexts detected.  In particular, I showed how you can define data types that are the union of other data types, possibly with intersecting interfaces.  By considering where an object is created in the code and what is on the system stack, code is generated to capture this context.  By putting the context directly into the code, the overhead of detecting the conditions in the objects is removed.&lt;/p&gt;

&lt;p&gt;Finally, I demonstrated a more open-ended approach that offers up some interesting future work: building class hierarchies on-the-fly.  The idea is to write programs without class definitions, but still use classes and objects.  I augmented a Lisp system to handle three cases: no class defined, no slot defined and no method found.  Using various clues, I come up with a class hierarchy and try it out.  There are various limitations to the approach (no surprise!) but it's really interesting and worth some future study.  That said, there are certain conditions where it produces reasonable class hierarchies.&lt;/p&gt;

&lt;p&gt;The external examiner on the thesis was Richard P. Gabriel, who asked some good questions and gave me some ideas with what to do next with the work.  In general, I got the impression the examination committee liked the work, even though it was imprecise in some areas.  There are only some minor revisions to make before officially submitting it to the university.&lt;/p&gt;

&lt;p&gt;I'm in the process of making the code available &amp;mdash; it needs some serious clean-up in places &amp;mdash; along with a tech report of the work.  When I have the revisions complete and its been submitted to the university, I'll post a copy of the thesis online.  Some may be interested in the introduction and literature survey.  (One examiner said I should look at publishing the survey.)  The more ambitious can dive into the technical elements.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5297067955878027454?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5297067955878027454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5297067955878027454' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5297067955878027454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5297067955878027454'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/08/behavioural-synthesis-lisp-thesis.html' title='Behavioural synthesis, the (Lisp) thesis'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-4261969358056839052</id><published>2008-07-20T08:59:00.008-05:00</published><updated>2008-07-20T16:00:19.315-05:00</updated><title type='text'>A Lisper's initial experience with Java</title><content type='html'>&lt;p&gt;I started a new job a little over a week ago wherein I have to write Java code, mostly writing tests in JUnit.  I will state up front that I do not have anything against Java.  I think it's a decently designed language considering when it was designed and its target audience.  I should also say that I'm impressed with Eclipse.  I doubt my experience with Java would be as enjoyable without it.  Kudos to the Eclipse team.  That said, I sorely miss the qualities of Lisp when trying to do my work.&lt;/p&gt;

&lt;p&gt;Now, I haven't done extensive work with Java yet since I am still getting up to speed on many things at my new place of employment.  Thus, it may be the case I don't know how to do certain things effectively in Java/JUnit.  Still, here are some things I really miss in Lisp when moving to Java.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Special variables&lt;/strong&gt;: Debugging in the system I'm using involves changing various static constants in classes.  This is fine if you have write access to things, but when you don't or when obtaining it may cause conflicts with the version control software, it's a pain.  I do not like that it is a compile-time constant.  &lt;code&gt;(let ((*debug* t)) ...)&lt;/code&gt; would be so much nicer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLOS and its MOP&lt;/strong&gt;: You don't realize the power of generic functions until you don't have them.  Some of the code I'm working on is old and contains, shall we say, questionable design decisions.  (To be fair, the old code would never pass current code reviews, but it's now production code, so we can't change it.)  Testing it is problematic because I have to extend existing (mock!) classes to get at the objects I need to examine at the right time.  Adding an around method to a particular method would be much cleaner.  Even better, adjusting the methods programatically while debugging would be great.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple inheritance&lt;/strong&gt;: The desire to use multiple inheritance in Java may pass as I get more accustomed to Java, but certain common methods would be better served if implemented as a mixin or something similar.  Right now, there's more code repetition than there should be, in my opinion.  However, it can't be factored out into separate classes very easily.&lt;/p&gt;

&lt;p&gt;Overall, what I dislike about using Java is the inability to poke and prod a running system to learn something about how it works.  Sure, you can use a debugger with breakpoints, but when an error occurs, I can't drop down to Java code and start making calls.  It's the interaction that's lovely about Lisp and Java just doesn't have it in the same way.  Partial information is powerful.  Interacting with a failure is powerful.  Being able to navigate the stack after an exception has been thrown while the system is still loaded is powerful.  They are powerful because they provide information that may otherwise be hard to obtain.  Instead of tracing through code, I can interact with data.  The latter is considerably more productive, in my experience.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-4261969358056839052?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/4261969358056839052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=4261969358056839052' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4261969358056839052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4261969358056839052'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/07/lispers-initial-experience-with-java.html' title='A Lisper&apos;s initial experience with Java'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-9139425010312609218</id><published>2008-07-08T07:58:00.003-05:00</published><updated>2008-07-08T08:31:02.701-05:00</updated><title type='text'>5th European Lisp Workshop</title><content type='html'>&lt;p&gt;I just attended the &lt;a href="http://elw2008.bknr.net/home"&gt;5th European Lisp Workshop&lt;/a&gt; at &lt;a href="http://2008.ecoop.org/"&gt;ECOOP 2008&lt;/a&gt;, organized by &lt;a href="http://www.lrde.epita.fr/~didier/about/news.php"&gt;Didier Verna&lt;/a&gt;.  As Didier pointed out in his introduction at the workshop, it’s the third Lisp meeting in Europe in a short period of time demonstrating that Lisp is alive and well.&lt;/p&gt;&lt;p&gt;The workshop started with a keynote by Mark Tarver, &lt;a href="http://elw2008.bknr.net/submission/31"&gt;“Lisp for the 21st century”&lt;/a&gt;.  His basic thesis is that Lisp’s problems are mostly social and outlined ten qualities work future work in Lisp needs in order to be relevant and noticable.  He then described his &lt;a href="http://www.lambdassociates.org/"&gt;Qi language&lt;/a&gt; (pronounced “Q-I”, something I didn’t know), which brings strong type checking to Lisp.  The end of his talk focussed on what he calls “computational adequacy” (one of his ten qualities) and how Qi fails in this regard.  Essentially, computational adequacy means a language having enough stuff to easily get things done that people want to do.  The main argument was that Lisp lags in this respect.  It wasn’t as inflammatory as you might think since Mark seems genuinely concerned with Lisp.&lt;/p&gt;&lt;p&gt;Michael Wessel then gave a talk about &lt;a href="http://elw2008.bknr.net/submission/24"&gt;descriptional logics and a way to use them in Lisp&lt;/a&gt; that is simpler than current approaches.  I found the talk a little confusing and dense, but managed to get some clarification from Michael afterwards.  He works on semantic web tools and we seem to be in agreement that multiple, specialized ontologies will probably be the norm and there need to be tools to create the ontologies, since no one wants to do it manually.&lt;/p&gt;&lt;p&gt;Next, &lt;a href="http://p-cos.net/"&gt;Pascal Costanza&lt;/a&gt; and &lt;a href="http://prog.vub.ac.be/~cherzeel/"&gt;Charlotte Herzeel&lt;/a&gt; talked about work done by their student Leonardo Uribe with &lt;a href="http://elw2008.bknr.net/submission/22"&gt;QLisp&lt;/a&gt;, a Lisp for the simulation of quantum computation.  They went into the history of parallelism in Lisp dialects, namely CmLisp, *Lisp and Paralation Lisp, and a little on how QLisp was implemented using them.  Charlotte talked a bit about adding parallelizing constructs to Lisp and why it is important.  I rather liked the talk, since I considered doing work on this a couple years ago, but got onto other things instead.&lt;/p&gt;&lt;p&gt;After Pascal and Charlotte, I gave my talk, &lt;a href="http://elw2008.bknr.net/submission/19"&gt;“Adaptive Libraries and Interactive Code Generation for Common Lisp”&lt;/a&gt;.  I showed one way to realize a library for objects representing many types and how to evaluate code using it.  Since there are ambiguities, I use interaction to resolve them and remember the context of the operation.  Then I generate code specializing the use of the library using the contexts so that the ambiguities are removed and just for fun, I generate the code interactively.  Didier asked for a demo, which I couldn’t give for a variety of reasons, the main one being that I didn’t have access to my code(*).  Once I submit my thesis, I’ll make a demo and put the code out there.  I like to think my talk went well, but I’m not the best judge of such things.&lt;/p&gt;&lt;p&gt;In the afternoon session, Rich Hickey presented his work on &lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt;.  I like what he’s done with it, especially his use of interfaces.  I think I’ll play around with it for some of my work instead of trying to use Java directly.  My short synopsis: Check out Clojure.&lt;/p&gt;&lt;p&gt;Lastly, Pascal gave a great talk entitled &lt;a href="http://elw2008.bknr.net/submission/28"&gt;“make-method-lambda Considered Harmful”&lt;/a&gt;.  It boiled down to this: &lt;tt&gt;make-method-lambda&lt;/tt&gt; is a macro-like facility the depends on runtime values, so you get unintuitive results when compiling versus interpreting — sometimes.  The proposed solution was to deprecate &lt;tt&gt;make-method-lambda&lt;/tt&gt; and use keyword arguments to &lt;tt&gt;call-method&lt;/tt&gt; in method combination.  The bonus is that you can now use closures for the lambda forms of methods programmatically.&lt;/p&gt;&lt;p&gt;Overall, I thoroughly enjoyed the workshop and commend Didier for his efforts.  The intimacy of the workshop allowed for better conversation than I have found at larger meetings.  Discussions at the breaks and in the evening were enjoyable as well.  My only regret is that I couldn’t stick around to attend the Dynamic Languages Symposium today; I have to head back home so I can start a new job.&lt;/p&gt;&lt;p&gt;As for the venue, I didn't care for Cyprus all that much.  The conference website claimed it was 3 km from my hotel to the conference venue, but it's more like 5 or 6.  I walked there on Monday morning and learned of the error the hard way.  There was lots of garbage on the side of the road and the cab ride back from the conference to my hotel seemed excessively expensive. Drivers there seem to totally ignore the concept of a lane.  I also wasn't impressed by Larnaca airport.  It wasn't awful, but it isn't making me want to come back.&lt;/p&gt;&lt;p&gt;I'm looking forward to &lt;a href="http://groups.google.com/group/comp.lang.lisp/msg/798a15ec0fcf939e"&gt;ILC 2009&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;(*) It’s a long story, but basically, I took my wife’s laptop instead of mine and didn’t have time to get my environment up and running on it before leaving.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-9139425010312609218?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/9139425010312609218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=9139425010312609218' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/9139425010312609218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/9139425010312609218'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/07/5th-european-lisp-workshop.html' title='5th European Lisp Workshop'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-7627181129837539121</id><published>2008-05-29T20:00:00.001-05:00</published><updated>2008-05-29T20:00:55.365-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='academic'/><category scheme='http://www.blogger.com/atom/ns#' term='code generation'/><title type='text'>European Lisp Workshop paper</title><content type='html'>&lt;p&gt;I submitted a paper to the &lt;a href="http://elw.bknr.net/2008/html/home.html"&gt;European Lisp Workshop&lt;/a&gt; at &lt;a href="http://2008.ecoop.org/"&gt;ECOOP 2008&lt;/a&gt; and got notice that it was accepted!  I'm glad to say that the reviewers provided constructive feedback and didn't write terse, dismissive reviews.&lt;/p&gt;

&lt;p&gt;The paper is entitled &lt;em&gt;Adaptive libraries and interactive code generation for Common Lisp&lt;/em&gt; and builds on some work I presented at &lt;a href="http://www.international-lisp-conference.org/2007/index"&gt;ILC 2007&lt;/a&gt;.  It is joint work with my thesis supervisors.  The paper describes a library for an abstract data type (called a &lt;em&gt;dynamic abstract data type&lt;/em&gt; or DADT) whose instances represent multiple abstract data types.  When the code is evaluated, instances of the DADT note what operations are done to them and where those operations take place in the source code.  Most importantly, when these instances are created, they note where in the code they were created.&lt;/p&gt;

&lt;p&gt;In some cases, operations may be ambiguous, in which case the programmer is prompted to indicate which data types the operation is to be applied to.  This takes place in the context of a certain place in the code and is remembered for disambiguation purposes in the future.&lt;/p&gt;

&lt;p&gt;After the profiling information is analyzed, instances of the DADT are grouped together based on where in the code they were created and a specific type is generated for that place; the type is just a class with a slot for each structure inside the DADT instances that were actually used.  Code to operate on that type is also produced.&lt;/p&gt;

&lt;p&gt;The code generated is not inserted automatically, however (although there is no reason it couldn't be).  Instead, the code is given to the editor to present to the programmer in much the same way as a macroexpansion.  The reason for this is that the indicators of where in the source code things happen is actually provided by the editor and not the compiler.  The reason for this was to make the changing of places less sensitive to edits.  This way, the code generation scheme fits better with the usual workflow of Lisp programming.&lt;/p&gt;

&lt;p&gt;I'm in the process of adjusting the paper based on the reviewers' comments, but &lt;a href="http://www.csd.uwo.ca/~wozniak/papers/ELW2008-draft.pdf"&gt;here is a draft version&lt;/a&gt; of the one I submitted, if you're interested.  I'll also post some code for this once I've submitted the final version (due July 11).  I'm also putting together my thesis and gearing up to start a new job, so I'm rather busy of late.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-7627181129837539121?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/7627181129837539121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=7627181129837539121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7627181129837539121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7627181129837539121'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/05/european-lisp-workshop-paper.html' title='European Lisp Workshop paper'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5868545810435255421</id><published>2008-04-27T09:43:00.000-05:00</published><updated>2008-04-27T09:44:10.651-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='runtime'/><category scheme='http://www.blogger.com/atom/ns#' term='automatic'/><category scheme='http://www.blogger.com/atom/ns#' term='code generation'/><title type='text'>Getting structure from behaviour</title><content type='html'>&lt;p&gt;Suppose you wrote the following (contrived) code snippet and tried to evaluate it.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;let&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;point &lt;span class="paren"&gt;(&lt;/span&gt;make-instance 'point &lt;span class="builtin"&gt;:xval&lt;/span&gt; 1 &lt;span class="builtin"&gt;:yval&lt;/span&gt; 2&lt;span class="paren"&gt;)))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;print &lt;span class="paren"&gt;(&lt;/span&gt;xval point&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;print &lt;span class="paren"&gt;(&lt;/span&gt;yval point&lt;span class="paren"&gt;)))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Without any definition for the class &lt;code&gt;point&lt;/code&gt; or function values associated with &lt;code&gt;xval&lt;/code&gt; or &lt;code&gt;yval&lt;/code&gt;, it will fail.  But can you learn something useful from trying to run it?  (Maybe even get it to print &lt;code&gt;1 2&lt;/code&gt;?)&lt;/p&gt;

&lt;p&gt;Looking at the code, it's reasonable (but not necessarily correct!) to think that is says something like, "I want to make an instance of a class called &lt;code&gt;point&lt;/code&gt;.  Assume it has two slots called &lt;code&gt;xval&lt;/code&gt; and &lt;code&gt;yval&lt;/code&gt; because I tend to name the slots in accordance with the initialization arguments.  And since I tend to associate accessor functions with symbols bearing the same name as the slots, the calls to &lt;code&gt;xval&lt;/code&gt; and &lt;code&gt;yval&lt;/code&gt; can be considered accessors."&lt;/p&gt;

&lt;p&gt;There are an awful lot of assumptions being made here based on conventions, but in my experience, it's not really that far from the intent.  So, &lt;em&gt;what if the system could take advantage of these kinds of conventions to help build structural definitions based on the use of the code?&lt;/em&gt;  That is something I find really interesting and is what I've been working on for the last little while (in addition to that whole teaching thing).&lt;/p&gt;

&lt;p&gt;The goal I set out for myself was to build class definitions dynamically based on the use of the code.  Essentially, I want to watch the execution of the code to make some reasonable guesses as to what the classes look like without having to define them ahead of time.  It's a bit like &lt;a href="http://web.media.mit.edu/~lieber/PBE/"&gt;programming by example&lt;/a&gt;, except I'm not interested in generalizing behaviour; instead, I want to derive structure &lt;em&gt;from&lt;/em&gt; behaviour.&lt;/p&gt;

&lt;p&gt;This presents a bit of a chicken-and-egg problem: How can you run code that relies on structure without having that structure?  One answer is to make the structure as you go along.  This means harvesting information from errors while taking advantage of convention and adjusting the definition of correctness.&lt;/p&gt;

&lt;p&gt;The last point may be cringe-inducing, but is pragmatically necessary.  Getting such code to be correct means defining what "correct" even means in this context.  Usually, correct is taken to mean "produces the desired output".  Here, the desired output is not what the program would produce given a suitable structure, but a suitable structure itself that may lead to the desired output.&lt;/p&gt;

&lt;p&gt;Thus, we trust that what is being said as what is intended.  Nothing is to be verified because we have nothing to verify against.  All that can be done is to present a structure that in turn is verified by some external agent, such as the developer.&lt;/p&gt;

&lt;p&gt;This is really a search through a very large space given some information to go on.  The output produced can be put into the program text to help refine the program, but the idea of deriving structure in this manner should not be seen as a kind of operational semantics in which production code can be run on.  There are just too many unknowns.&lt;/p&gt;

&lt;p&gt;A reasonable question at this point is, "Why bother?"  First and foremost, I think it's an interesting challenge and I'm curious to see how far it can be taken.  More practically, if you could get reasonable structural definitions from somewhat complicated behavioural descriptions, it might be interesting to incorporate it with testing in some fashion, such as writing tests to produce programs (or significant parts of programs) that pass those tests.&lt;/p&gt;

&lt;p&gt;This stems from my annoyance with having to write and amend structural definitions all the time during program development.  (This is basically why I stopped using OCaml and its ilk in my day-to-day work.)  When writing programs I tend to specify the behaviour first, then fill in the structure because the act of figuring out what I want the program to do helps me figure out what the data should look like.&lt;/p&gt;

&lt;p&gt;Over the next few days, I'll be posting more about this, including how I've gotten it to work in a somewhat limited (although mildly interesting) context.  It's just too much to put in one post. :)&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5868545810435255421?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5868545810435255421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5868545810435255421' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5868545810435255421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5868545810435255421'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/04/getting-structure-from-behaviour.html' title='Getting structure from behaviour'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-74936655484077736</id><published>2008-04-26T17:55:00.000-05:00</published><updated>2008-04-26T17:56:02.265-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scheme'/><category scheme='http://www.blogger.com/atom/ns#' term='teaching'/><title type='text'>Done with teaching!</title><content type='html'>&lt;p&gt;I spent the last three and a half months &lt;a href="http://www.csd.uwo.ca/courses/CS342b/"&gt;teaching a programming languages course&lt;/a&gt; and working on my dissertation, which is why I haven't posted much here of late. The worst part about the teaching job was that it was in a different city than my home and had an awkward schedule, so I had to commute and stay at someone else's house for the majority of the week. It also meant I couldn't make any of the Toronto Lisp Meetings.&lt;/p&gt;

&lt;p&gt;I used Shriram Krishnamurthi's text &lt;a href="http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/"&gt;Programming Languages: Application and Interpretation&lt;/a&gt; for the course, which seemed to go over well, despite difficulties. The students coming into the course are mostly exposed only to Java and C/C++, so Scheme tends to break a few brains at the beginning. I'd like to say the whole class had a firm grasp on Scheme when it was over, but I can't.&lt;/p&gt;

&lt;p&gt;For the most part, students could write Scheme code, but it was mostly by translating (not very good) Java to Scheme. I'm not sure why. For example, there was an awful lot of this in assignment submissions:&lt;/p&gt;

&lt;pre&gt;(if (predicate? foo)
    (begin
      (set! ret-val (compute-something ...))
      ret-val)
    ...)
&lt;/pre&gt;

&lt;p&gt;To say that I stressed the idea of expressions evaluating to a value and avoiding mutation in my lectures, labs and through assignment feedback would be an understatement.  I'm not sure why this mindset persisted, but I hypothesize it has something to do with wanting to make the simple complex.  Or maybe it has something to do with not seeing the forest for the trees.  I really don't know.&lt;/p&gt;

&lt;p&gt;I had a difficult time getting across the idea of trusting an abstraction, even when comparing to something like the APIs used frequenty in the Java libraries.  For example, many students just couldn't deal with continuations without knowing what happened inside them.  One even remarked he wished it had a &lt;code&gt;toString&lt;/code&gt; method.  It took a lot of convincing that seeing &lt;code&gt;#&amp;lt;continuation&amp;gt;&lt;/code&gt; in DrScheme was basically the same thing.&lt;/p&gt;

&lt;p&gt;Student: "But I don't know what's happening inside it!"&lt;/p&gt;

&lt;p&gt;Me: "That's the point: it doesn't really matter."&lt;/p&gt;

&lt;p&gt;The same symptom could be seen in trying to understand functions as values, except that I flogged the notion of closures so much that some students' brains couldn't help but give up and understand it. :)&lt;/p&gt;

&lt;p&gt;Another thing that struck me was how, after explaining macros in Scheme, most tried to write macros as you would in Common Lisp.  I expect its because CL macros let you manipulate the code directly, which fits with the theme of getting at the guts of something.  Maybe there's something deeper at work here, but I'm not going to go wild in my conjectures based on this small sample.&lt;/p&gt;

&lt;p&gt;After this class, I'm more convinced that starting with something like Scheme followed by Java would be an easier transition than the other way around.  Whether starting with Scheme is easier than Java is another question entirely.&lt;/p&gt;

&lt;p&gt;Oddly enough, they seemed to grasp Prolog pretty well...&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-74936655484077736?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/74936655484077736/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=74936655484077736' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/74936655484077736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/74936655484077736'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/04/done-with-teaching.html' title='Done with teaching!'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-3698117872735627687</id><published>2008-02-19T10:53:00.003-05:00</published><updated>2008-02-19T10:58:56.758-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='comparison'/><category scheme='http://www.blogger.com/atom/ns#' term='runtime'/><category scheme='http://www.blogger.com/atom/ns#' term='restarts'/><title type='text'>Continuing and restarts</title><content type='html'>&lt;p&gt;It's been a while since I have posted anything, mostly due to the fact that I'm teaching an &lt;a href="http://www.csd.uwo.ca/courses/CS342b/"&gt;undergraduate course on programming languages&lt;/a&gt;.  Since today is a new holiday (ok, yesterday, technically) in my province ("&lt;a href="http://www.labour.gov.on.ca/english/es/family/index.html"&gt;Family Day&lt;/a&gt;"), I actually have a little free time.  That hasn't happened in a while.&lt;/p&gt;

&lt;p&gt;Lately, I've been working on an approach to dealing with certain kinds of errors such that evaluation continues, even though it may not be entirely correct.  This has me working with one of my favourite features of Lisp or any other language: restarts.  Restarts are not only a wonder to behold, but provide a lot of potential.  The primary reason, in my opinion, is that you can continue computation in the face of some problem that doesn't have an immediately obvious solution.&lt;/p&gt;

&lt;p&gt;That being said, the usefulness of restarts is limited by those who provide them.  In your own code, you can add restarts as you see fit, but if you want to try and recover from problems indicated by the Lisp implementation, you're at the mercy of the Lisp implementor.&lt;/p&gt;

&lt;p&gt;What restarts are made available and when are rarely (if ever) defined in the standard.  So I thought it might be worthwhile to provide a partial survey of situations and whether an implementation provides the &lt;code&gt;continue&lt;/code&gt; restart (or something similar, such as &lt;code&gt;retry&lt;/code&gt;) so that you can correct the situation and continue, either interactively or within a program.&lt;/p&gt;

&lt;table border="0" cellspacing="5" cellpadding="5"&gt;
  &lt;tr&gt;&lt;th rowspan="2" valign="top"&gt;Situation&lt;/th&gt;&lt;th colspan="4" valign="top"&gt;Lisp implementations&lt;sup&gt;0&lt;/sup&gt;&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;th&gt;Allegro CL&lt;/th&gt;&lt;th&gt;Lispworks&lt;/th&gt;&lt;th&gt;SBCL&lt;/th&gt;&lt;th&gt;CLISP&lt;/th&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;No function defined&lt;sup&gt;1&lt;/sup&gt;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Failed &lt;code&gt;function&lt;/code&gt; lookup&lt;sup&gt;2&lt;/sup&gt;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;No class found&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Division by zero&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;No method found&lt;sup&gt;3&lt;/sup&gt;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;No slot found&lt;sup&gt;3&lt;/sup&gt;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Replace function with generic function&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;/tr&gt;
  &lt;tr&gt;&lt;td&gt;Redefine a generic function&lt;sup&gt;4&lt;/sup&gt;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;&amp;#x2713;&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;td align="center"&gt;-&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;sup&gt;0&lt;/sup&gt; The versions tested were ACL 8.1 Enterprise, Lispworks 5.0 Personal, SBCL 1.0.13.53, and CLISP 2.4.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; Use case: evaluate a form such as &lt;code&gt;(foo 0)&lt;/code&gt; where &lt;code&gt;foo&lt;/code&gt; is not &lt;code&gt;fbound&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;2&lt;/sup&gt; Evaluate &lt;code&gt;(function foo)&lt;/code&gt; where &lt;code&gt;foo&lt;/code&gt; is not &lt;code&gt;fbound&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;3&lt;/sup&gt; In these situations, it is not necessary to offer a continuable restart, since there are ways to deal with this using the MOP.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;4&lt;/sup&gt; Use case: define a generic function, then evaluate some &lt;code&gt;defmethod&lt;/code&gt; forms and make an incompatible change to the generic function definition using either &lt;code&gt;defgeneric&lt;/code&gt; or &lt;code&gt;defmethod&lt;/code&gt;.  Alternatively, define a method without a generic function definition, then make an incompatible change to generic function.&lt;/p&gt;


&lt;p&gt;If an implementation doesn't offer a restart for &lt;code&gt;SETF&lt;/code&gt; functions (which usually means it doesn't offer one for &lt;code&gt;function&lt;/code&gt; lookup), you can add your own by customizing &lt;code&gt;*macroexpand-hook*&lt;/code&gt; to wrap the expansion in something that provides a way to continue, perhaps like the following, assuming that the &lt;code&gt;(setf (foo (list 1)) 2)&lt;/code&gt; is replaced with the actual expanded form.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;block&lt;/span&gt; #1=#&lt;span class="builtin"&gt;:BLOCK-1000&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;tagbody&lt;/span&gt;
     #2=#&lt;span class="builtin"&gt;:START-1001&lt;/span&gt;
     &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;handler-bind&lt;/span&gt;
         &lt;span class="paren"&gt;((&lt;/span&gt;undefined-function #'&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;lambda&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;#3=#&lt;span class="builtin"&gt;:CONDITION-1002&lt;/span&gt;&lt;span class="paren"&gt;)&lt;/span&gt;
                                  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="warning"&gt;cerror&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;format nil &lt;span class="string"&gt;"Try calling ~A again."&lt;/span&gt;
                                                  &lt;span class="paren"&gt;(&lt;/span&gt;cell-error-name #3#&lt;span class="paren"&gt;))&lt;/span&gt;
                                           &lt;span class="string"&gt;"~A is not fbound."&lt;/span&gt;
                                           &lt;span class="paren"&gt;(&lt;/span&gt;cell-error-name #3#&lt;span class="paren"&gt;))&lt;/span&gt;
                                  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;go&lt;/span&gt; #2#&lt;span class="paren"&gt;))))&lt;/span&gt;
        &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;return-from&lt;/span&gt; #1# &lt;span class="paren"&gt;(&lt;/span&gt;setf &lt;span class="paren"&gt;(&lt;/span&gt;foo &lt;span class="paren"&gt;(&lt;/span&gt;list 1&lt;span class="paren"&gt;))&lt;/span&gt; 2&lt;span class="paren"&gt;)))))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Doing this for everything that is macroexpanded would be a bit much, so you should probably only do it in certain circumstances.&lt;/p&gt;

&lt;p&gt;Currently, these are the only situations I'm concerned with.  Well, almost &amp;mdash; I threw in the division by zero one since it's a canonical example of an error.&lt;/p&gt;

&lt;p&gt;It's hard not to notice that the commercial implementations listed offer a lot more in the way of restarts than the free ones listed.  I'm sure there is a reason for this, probably related to customer demands.  I will say that the restarts offered by Allegro and LispWorks have caused me to start using them instead of SBCL.  Doing what I want to do in SBCL requires a lot more hackery (either changing SBCL itself or using some techniques I'd rather not use).  Right now, I'm using Allegro CL Enterprise since I got an equipment grant for it with AllegroGraph, but LispWorks is looking pretty good too.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-3698117872735627687?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/3698117872735627687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=3698117872735627687' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3698117872735627687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3698117872735627687'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/02/continuing-and-restarts.html' title='Continuing and restarts'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-1040640096716769321</id><published>2008-01-11T23:07:00.001-05:00</published><updated>2008-01-11T23:11:52.150-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='runtime'/><category scheme='http://www.blogger.com/atom/ns#' term='automatic'/><category scheme='http://www.blogger.com/atom/ns#' term='code generation'/><title type='text'>Auto-defining functions</title><content type='html'>&lt;p&gt;Here's a puzzle I've been playing around with lately: How would you go about defining functions automatically in Common Lisp?&lt;/p&gt;

&lt;p&gt;Try to put out of your mind &lt;em&gt;why&lt;/em&gt; you would want to do this for the time being (I'll make my reasons for doing so known in another post).&lt;/p&gt;

&lt;p&gt;First of all, some definitions and ground rules.  "Auto-defining a function" means that if you have code executing and an attempt to evaluate a form signals an &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/e_undefi.htm#undefined-function"&gt;&lt;code&gt;undefined function&lt;/code&gt;&lt;/a&gt;, a function is defined for the symbol and called without interrupting the evaluation.  That is to say, the debugger is not entered and execution continues.&lt;/p&gt;

&lt;p&gt;(Yes, this is fraught with potential problems, like making a function that does what was intended.  Again, I'll worry about this later.)&lt;/p&gt;

&lt;p&gt;The hurdle in this is to be able to &lt;em&gt;continue&lt;/em&gt; the execution.  I'll take advantage of restarts to accomplish this, but it requires that the Lisp implementation supply a continuable restart at the correct time.  Allegro CL does, so that's what I'll use.&lt;/p&gt;

&lt;p&gt;The general principle can be seen in this interaction at the REPL.&lt;/p&gt;

&lt;pre class="repl"&gt;
  &lt;span class="slime-repl-prompt"&gt;CL-USER(1):&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(square 5)&lt;/span&gt;
  Error: attempt to call `SQUARE' which is an undefined function.
    [condition type: UNDEFINED-FUNCTION]

  Restart actions (select using :continue):
   0: Try calling SQUARE again.
   1: Return a value instead of calling SQUARE.
   2: Try calling a function other than SQUARE.
   3: Setf the symbol-function of SQUARE and call it again.
   4: Return to Top Level (an "abort" restart).
   5: Abort entirely from this (lisp) process.
  &lt;span class="slime-repl-prompt"&gt;[1] CL-USER(2):&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(setf (fdefinition 'square) #'(lambda (x) (* x x)))&lt;/span&gt;
  &lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;#&amp;lt;Interpreted Function FOO&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="slime-repl-prompt"&gt;[1] CL-USER(3):&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(invoke-restart 'excl::try-again)&lt;/span&gt;
  &lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;25&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;The above is actually possible using one of the provided interactive restarts, but the goal is to avoid interaction.  The name of the restart (&lt;code&gt;excl::try-again&lt;/code&gt;) was obtained using &lt;code&gt;compute-restarts&lt;/code&gt; and inspecting the results, which was not shown.&lt;/p&gt;

&lt;p&gt;To do this programmatically, you need to set up a handler for &lt;code&gt;undefined-function&lt;/code&gt; and do the above.  Again, ignoring the notion of actually building a "correct" function, there is a small problem: &lt;code&gt;undefined-function&lt;/code&gt; only tells you the name of the function and nothing about the arguments.&lt;/p&gt;

&lt;p&gt;Getting around this can be done using a two stage process: define a dummy function that works for any number of arguments that does the real work of defining the function.  This prevents the messiness that would result from trying to get this information by inspecting the stack, for example.&lt;/p&gt;

&lt;p&gt;For demonstration purposes, I'll only be concerned with auto-defining the &lt;code&gt;square&lt;/code&gt; function.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;auto-definable-function-p&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;name args&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;and &lt;span class="paren"&gt;(&lt;/span&gt;eql name 'square&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;= 1 &lt;span class="paren"&gt;(&lt;/span&gt;length args&lt;span class="paren"&gt;))))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;auto-define-function-stage-one&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;name&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;setf &lt;span class="paren"&gt;(&lt;/span&gt;fdefinition name&lt;span class="paren"&gt;)&lt;/span&gt;
        #'&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;lambda&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; args&lt;span class="paren"&gt;)&lt;/span&gt;
            &lt;span class="paren"&gt;(&lt;/span&gt;auto-define-function-stage-two name args&lt;span class="paren"&gt;))))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;auto-define-function-stage-two&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;name args&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;cond&lt;/span&gt;
    &lt;span class="paren"&gt;((&lt;/span&gt;auto-definable-function-p name args&lt;span class="paren"&gt;)&lt;/span&gt;
     &lt;span class="paren"&gt;(&lt;/span&gt;setf &lt;span class="paren"&gt;(&lt;/span&gt;fdefinition name&lt;span class="paren"&gt;)&lt;/span&gt; #'&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;lambda&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;arg&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;* arg arg&lt;span class="paren"&gt;)))&lt;/span&gt;
     &lt;span class="paren"&gt;(&lt;/span&gt;apply name args&lt;span class="paren"&gt;))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;t &lt;span class="paren"&gt;(&lt;/span&gt;fmakunbound name&lt;span class="paren"&gt;)&lt;/span&gt;
       &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="warning"&gt;error&lt;/span&gt; 'undefined-function &lt;span class="builtin"&gt;:name&lt;/span&gt; name&lt;span class="paren"&gt;))))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;handle-undefined-function&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;condition&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;find-restart 'excl::try-again condition&lt;span class="paren"&gt;)&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;auto-define-function-stage-one &lt;span class="paren"&gt;(&lt;/span&gt;cell-error-name condition&lt;span class="paren"&gt;))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;invoke-restart 'excl::try-again&lt;span class="paren"&gt;)))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;try-undefined&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="type"&gt;&amp;amp;body&lt;/span&gt; body&lt;span class="paren"&gt;)&lt;/span&gt;
  `&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;handler-bind&lt;/span&gt;
       &lt;span class="paren"&gt;((&lt;/span&gt;undefined-function &lt;span class="paren"&gt;(&lt;/span&gt;function handle-undefined-function&lt;span class="paren"&gt;)))&lt;/span&gt;
     ,@body&lt;span class="paren"&gt;))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Here it is at work.&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(fboundp 'square)&lt;/span&gt;
&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(trace auto-define-function-stage-one)&lt;/span&gt;
&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;(AUTO-DEFINE-FUNCTION-STAGE-ONE)&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(trace auto-define-function-stage-two)&lt;/span&gt;
&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;(AUTO-DEFINE-FUNCTION-STAGE-TWO)&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(try-undefined (square 25))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt; 0[7]: (AUTO-DEFINE-FUNCTION-STAGE-ONE SQUARE)
 0[7]: returned
         #&amp;lt;Closure (:INTERNAL AUTO-DEFINE-FUNCTION-STAGE-ONE
                    0) [SQUARE]
           @ #x105642c2&amp;gt;
 0[7]: (AUTO-DEFINE-FUNCTION-STAGE-TWO SQUARE (25))
 0[7]: returned 625
&lt;/span&gt;&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;625&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(fboundp 'square)&lt;/span&gt;
&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;#&amp;lt;Function (:INTERNAL AUTO-DEFINE-FUNCTION-STAGE-TWO 0)&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(square 100)&lt;/span&gt;
&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;10000&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;This is clearly a rough implementation and only a proof of concept, but it provides a foothold for further work.  I've offered no compelling reason for actually doing this; I've only shown that it is technically possible to do so.  I'll elaborate on the why soon enough.  (It may be a bit delayed because I've started lecturing a programming languages course and I first have to convince my students that Scheme &amp;mdash; as foreign as it is to them &amp;mdash; isn't as awful as they may think!)&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-1040640096716769321?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/1040640096716769321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=1040640096716769321' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1040640096716769321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1040640096716769321'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2008/01/auto-defining-functions.html' title='Auto-defining functions'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-8286478826963530932</id><published>2007-12-17T18:11:00.001-05:00</published><updated>2007-12-17T18:11:43.730-05:00</updated><title type='text'>The Revised Maclisp Manual</title><content type='html'>Some fun reading for programming language history buffs: the &lt;a href="http://www.maclisp.info/pitmanual/index.html"&gt;Revised Maclisp Manual&lt;/a&gt; is now available on the web, care of Kent M. Pitman.

Thanks Kent!  Now I'll be sufficiently distracted for the next little while.



&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-8286478826963530932?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/8286478826963530932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=8286478826963530932' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/8286478826963530932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/8286478826963530932'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/12/revised-maclisp-manual.html' title='The Revised Maclisp Manual'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-1350152487074510400</id><published>2007-11-24T09:31:00.001-05:00</published><updated>2007-11-24T09:31:48.545-05:00</updated><title type='text'>My favourite RtL survey answer</title><content type='html'>From &lt;a href="http://dlweinreb.wordpress.com/"&gt;Dan Weinreb's&lt;/a&gt; &lt;a href="http://wiki.alu.org:80/Daniel_Weinreb's_Road_to_Lisp"&gt;Road to Lisp entry&lt;/a&gt;: 

&lt;blockquote&gt;
&lt;strong&gt;How far have you gotten in your study of Lisp?&lt;/strong&gt; Pretty far. I am one of the designers of Common Lisp.
&lt;/blockquote&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-1350152487074510400?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/1350152487074510400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=1350152487074510400' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1350152487074510400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1350152487074510400'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/11/my-favourite-rtl-survey-answer.html' title='My favourite RtL survey answer'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-7773206481780300075</id><published>2007-11-22T22:26:00.001-05:00</published><updated>2007-11-22T22:26:51.778-05:00</updated><title type='text'>More code walking</title><content type='html'>&lt;p&gt;One of the tricky aspects to understanding and writing a code-walker is knowing how and when to expand macros.&lt;/p&gt;

&lt;p&gt;When you are walking code, it's necessary to keep track of what is in the current lexical environment so that you know when a symbol names a function or a macro.  If it names a macro, you have to call the necessary macro function.&lt;/p&gt;

&lt;p&gt;In order to call the macro function, you have to evaluate it when you come to it.  Ignoring &lt;code&gt;defmacro&lt;/code&gt; and &lt;code&gt;define-symbol-macro&lt;/code&gt; for the time being, you still have a bit of a problem with &lt;code&gt;macrolet&lt;/code&gt; and &lt;code&gt;symbol-macrolet&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The trouble lies in the evaluation of the macro function.  To illustrate, here's an example.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;macrolet&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;foo &lt;span class="paren"&gt;()&lt;/span&gt; `&lt;span class="paren"&gt;(&lt;/span&gt;print 1&lt;span class="paren"&gt;)))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;foo&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;macrolet&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;bar &lt;span class="paren"&gt;()&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;foo&lt;span class="paren"&gt;)&lt;/span&gt; `&lt;span class="paren"&gt;(&lt;/span&gt;print 2&lt;span class="paren"&gt;)))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;bar&lt;span class="paren"&gt;)))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;When you walk the macro definition for &lt;code&gt;foo&lt;/code&gt;, you have to construct the macro function for it so that it is available to the enclosed body of code.  A simple way to do this is to actually call &lt;code&gt;eval&lt;/code&gt; on the body of the macro definition, say, by creating a &lt;code&gt;defmacro&lt;/code&gt; form and passing it to &lt;code&gt;eval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Say you do this and keep processing forms.  When you get to the &lt;code&gt;(foo)&lt;/code&gt; form, you know that &lt;code&gt;foo&lt;/code&gt; is a macro so you expand it and substitute &lt;code&gt;(foo)&lt;/code&gt; with &lt;code&gt;(print 1)&lt;/code&gt;.  No problem there.&lt;/p&gt;

&lt;p&gt;Now you get to the definition for the local macro &lt;code&gt;bar&lt;/code&gt;.  You still have to walk the macro definition so that you can expand the &lt;code&gt;foo&lt;/code&gt; macro.  The HyperSpec says that with &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm"&gt;&lt;code&gt;macrolet&lt;/code&gt;&lt;/a&gt;, it is undefined if the body of the macro references any local variable or function bindings, but macros (local or global) must be expanded.&lt;/p&gt;

&lt;p&gt;When I tried this code out with the arnesi code-walker under ACL and SBCL, it failed.  The error it gives me is that it is trying to call a function &lt;code&gt;foo&lt;/code&gt;.  Using some traces, I found that when the macro function for &lt;code&gt;bar&lt;/code&gt; is being defined, the lexical environment is empty.  Thus, the macro function produced considers &lt;code&gt;foo&lt;/code&gt; to be a function.&lt;/p&gt;

&lt;p&gt;Oddly enough, ACL's code-walker (see the symbol &lt;code&gt;excl::walk&lt;/code&gt;) gave me the same error!  I don't know why yet, but it evaluates and compiles it just fine.  SBCL didn't have a problem with it.&lt;/p&gt;

&lt;p&gt;Goes to show you that writing these code walkers is tricky.  And I didn't even mention anything about environment objects...&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-7773206481780300075?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/7773206481780300075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=7773206481780300075' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7773206481780300075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/7773206481780300075'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/11/more-code-walking.html' title='More code walking'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-2288287436986880901</id><published>2007-11-21T18:40:00.001-05:00</published><updated>2007-11-21T18:40:58.991-05:00</updated><title type='text'>Code walking caveats</title><content type='html'>&lt;p&gt;As Richard C. Waters has &lt;a href="http://www.merl.com/publications/TR1993-017/"&gt;pointed out&lt;/a&gt;, it often handy to have a program that can walk Lisp code to be able to analyse it.  In my case, I've tried to avoid the need for a code-walker, but the time has come in my work to make use of one.  (Compiler macros can only take you so far.)&lt;/p&gt;

&lt;p&gt;There are plenty of &lt;a href="http://bc.tech.coop/blog/040512.html"&gt;code walking programs&lt;/a&gt; out there, so I'm not overly concerned with implementing it.  What I'm more concerned with is idiosyncrasies across implementations.&lt;/p&gt;

&lt;p&gt;The first one I've found is in SBCL (1.0.11.22).  When you walk a &lt;code&gt;DEFUN&lt;/code&gt; form and expand the macros, you get something that fools some code-walkers.  For example,&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;example&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;x&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;+ x x&lt;span class="paren"&gt;))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;eventually expands into&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;PROGN&lt;/span&gt;
 &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;EVAL-WHEN&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="builtin"&gt;:COMPILE-TOPLEVEL&lt;/span&gt;&lt;span class="paren"&gt;)&lt;/span&gt;
   &lt;span class="paren"&gt;(&lt;/span&gt;SB-C:%COMPILER-DEFUN 'EXAMPLE 'NIL T&lt;span class="paren"&gt;))&lt;/span&gt;
 &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;EVAL-WHEN&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="builtin"&gt;:LOAD-TOPLEVEL&lt;/span&gt; &lt;span class="builtin"&gt;:EXECUTE&lt;/span&gt;&lt;span class="paren"&gt;)&lt;/span&gt;
   &lt;span class="paren"&gt;(&lt;/span&gt;SB-IMPL::%DEFUN
    'EXAMPLE
    &lt;span class="paren"&gt;(&lt;/span&gt;FUNCTION
     &lt;span class="paren"&gt;(&lt;/span&gt;SB-INT:NAMED-LAMBDA EXAMPLE &lt;span class="paren"&gt;(&lt;/span&gt;X&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;BLOCK&lt;/span&gt; EXAMPLE &lt;span class="paren"&gt;(&lt;/span&gt;+ X X&lt;span class="paren"&gt;))))&lt;/span&gt;
    NIL 'NIL &lt;span class="paren"&gt;(&lt;/span&gt;SB-C:SOURCE-LOCATION&lt;span class="paren"&gt;))))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;The key here is the &lt;code&gt;NAMED-LAMBDA&lt;/code&gt; part.  Note that it is found within a &lt;code&gt;FUNCTION&lt;/code&gt; form.  &lt;code&gt;FUNCTION&lt;/code&gt; is a special operator which takes either a function name or a lambda expression.  The &lt;code&gt;SB-INT:NAMED-LAMBDA&lt;/code&gt; form doesn't qualify as either, so I've got myself a classic special case particular to an implementation. (Although it is clearly a special kind of lambda form.)&lt;/p&gt;

&lt;p&gt;The reason this is important is that I want to get at the &lt;code&gt;BLOCK&lt;/code&gt; part which holds the body of the function.  The &lt;a href="http://common-lisp.net/project/bese/arnesi.html"&gt;arnesi&lt;/a&gt; walker (the one I've been using) doesn't walk the &lt;code&gt;NAMED-LAMBDA&lt;/code&gt; part unless the form is a &lt;code&gt;LAMBDA&lt;/code&gt;.  On the other hand, the &lt;a href="http://www-cgi.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/lisp/code/codewalk/walk/0.html"&gt;PCL&lt;/a&gt; walker does (as far as I can tell), which is what SBCL's is based on.  Allegro CL's walker behaves similar to arnesi's.&lt;/p&gt;

&lt;p&gt;So it seems that with choosing a code-walker (or writing one), I'll have to make it handle things that don't quite mesh with the HyperSpec.&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-2288287436986880901?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/2288287436986880901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=2288287436986880901' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/2288287436986880901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/2288287436986880901'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/11/code-walking-caveats.html' title='Code walking caveats'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-3343477828179295906</id><published>2007-10-25T14:12:00.000-05:00</published><updated>2007-10-25T14:30:06.086-05:00</updated><title type='text'>Specializing object representations</title><content type='html'>&lt;p&gt;Using profiling information to adjust program code has been an obsession of mine lately.  Ever since I started programming, I&amp;#8217;ve been interested in program changes over time based on the program&amp;#8217;s previous performance.  Over the last few months, I&amp;#8217;ve been working on a way to feed profiling information back to a Lisp compiler to specialize code.&lt;/p&gt;

&lt;p&gt;Specifically, I&amp;#8217;ve been experimenting with specializing object representations.  Suppose you know that an object is of some generic type, like a sequence, but when used in a program, it is always a string.  Furthermore, this information is known to some kind of profiler, which could be an external program or even part of the program itself.  Lastly, the information collected by the profiler is available to the compiler.  Assuming the information is reliable, the compiler could specialize the sequence object to be a string.  To speed up execution, the compiler could also change operations done on the object to be specific to strings (such as changing &lt;code&gt;ELT&lt;/code&gt; to &lt;code&gt;CHAR&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Note that I am describing source level changes to the code.  Also, I am assuming that the program is profiled in a comprehensive manner.&lt;/p&gt;

&lt;p&gt;I'll quickly describe the way I've done this without delving into too many details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Profiling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is important to be clear about the scope of objects being specialized in this context.  I decided to break down objects according to some general type.  I chose to work with sets since they are non-native data types (save for treating lists as sets) and admit many possible implementations.  There was a single interface used for all implementations.&lt;/p&gt;

&lt;p&gt;The next step was to figure out the scope of those objects within the program itself.  I thought that considering the entire program at once to be too broad, so I settled on only considering the places in the code where set objects are created.  For simplicity, I added a function to the interface that makes set objects.  I was only concerned with static calls for the time being.&lt;/p&gt;

&lt;p&gt;The only hurdle left was collecting information that could be used by the compiler.  How do you identify places in the source code to the compiler?  I came up with a hack that tends to work, but that I wouldn&amp;#8217;t count on.&lt;/p&gt;

&lt;p&gt;I define compiler macros for the interface functions I am interested in tracking.  When profiling code is to be generated, a counter is incremented in the environment of the compiler macros.  The value of this counter is the identifier for the place in the source code, known as the &lt;em&gt;lexical place identifier&lt;/em&gt;.  It is reset each time the compiler is run.  This relies on the compiler walking the source code in the same way each time and that no changes are made to the source between compilations.&lt;/p&gt;

&lt;p&gt;Finally, to track information about objects, I annotate the objects themselves.  Each object is tagged with the lexical place identifier of where it was created.  Using the object annotations, information about the use of the object is collected into the environment of the compiler macros.&lt;/p&gt;

&lt;p&gt;As an example, suppose the function &lt;code&gt;MAKE-SET&lt;/code&gt; creates set objects.  I define a compiler macro for it that, when profiling is enabled, takes immediate calls written as&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;make-set&lt;span class="paren"&gt;)&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;and turns them into something like the following.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;locally&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;declare&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;notinline make-set&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;let&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;obj &lt;span class="paren"&gt;(&lt;/span&gt;make-set&lt;span class="paren"&gt;)))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;initialize-profiling obj 1&lt;span class="paren"&gt;)&lt;/span&gt;
    obj&lt;span class="paren"&gt;))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;The 1 here is really the value of the lexical place identifier.  It would be different for each call site.  The declaration is used to prevent any further invocation of the compiler macro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specialization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once profiling information has been collected, the compiler is run again with specialization turned on.  In order to know how to specialize the call site, the lexical place identifier is used.  During profiling, each object writes information to a database keyed on the lexical place identifier.  Thus, when the compiler macro goes to specialize a call, it looks up the information associated with the lexical place identifier, analyzes it, and generates code accordingly.&lt;/p&gt;

&lt;p&gt;Using the example above, suppose analysis showed that set objects created at lexical place 1 were usually very small and that lots of them were created.  This suggests using sets that are implemented as lists.  The compiler macro for &lt;code&gt;MAKE-SET&lt;/code&gt; would then produce something to create sets represented as lists at lexical place 1, such as&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;make-instance 'list-set&lt;span class="paren"&gt;)&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;possibly with extra initialization arguments, such as a specific test function.&lt;/p&gt;

&lt;p&gt;This only deals with the lexical places that create set objects directly.  It is also possible to use this approach to specialize direct uses of the objects.  (I&amp;#8217;ve done this with a very general map implementation, where direct access is used when the field is known).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveats and stuff to do&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The whole thing relies on comprehensive inputs for profiling.  Obviously, there is a risk here.  It also needs source code to be processed in exactly the same way for each compilation and that the source code doesn&amp;#8217;t change.  Overall, the system is a bit brittle, but it mostly works.&lt;/p&gt;

&lt;p&gt;The hack to identify lexical places to the compiler is just that: a hack.  The version I have works with SBCL and ACL, but there is an issue with LispWorks.  I wanted to avoid having to write my own source analyzer and use the compiler macro mechanism instead.  This allowed me to be somewhat implementation agnostic.  I don&amp;#8217;t know if it scales or not and wouldn&amp;#8217;t be surprised if it didn&amp;#8217;t.&lt;/p&gt;

&lt;p&gt;ACL has an identifier in their environment objects that could be used instead of the counter.  I haven&amp;#8217;t tried it out yet, but it seems promising.  Some sort of file identifier would have to be issued as well.&lt;/p&gt;

&lt;p&gt;Currently, I only look for static calls to the interface functions.  The notion of lexical places could work in a dynamic environment too, but you would have to use something other than just compiler macros.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started out trying to implement something to model very abstract concepts (sets, maps, graphs and grammars) in such a way that the user doesn&amp;#8217;t have to be concerned with implementation details, even more than usual.  Really, I was interested in very simple interfaces.&lt;/p&gt;

&lt;p&gt;The problem is that a single implementation is rarely appropriate for these structures.  However, when an object can take on multiple representations, the overhead can be a problem and the logic is difficult to capture.  So I thought it might be interesting to try and come up with ways that objects could be profiled such that either the object can change its representation or the compiler could pick one that is suitable for how it is used.&lt;/p&gt;

&lt;p&gt;In the end, I&amp;#8217;m aiming to build something where representation choice can be made by the programming environment for simple to moderately complex situations without any necessary user intervention and still have decent performance.  Kind of like automatic memory management, but for data structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finally&amp;#8230;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code isn&amp;#8217;t ready for public release yet, but if anyone is interested, feel free to get in touch with me.  If I can&amp;#8217;t write it as a library, I&amp;#8217;m thinking about integrating it into SBCL as an extension.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-3343477828179295906?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/3343477828179295906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=3343477828179295906' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3343477828179295906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3343477828179295906'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/10/specializing-object-representations.html' title='Specializing object representations'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-4312942664979092136</id><published>2007-09-24T06:45:00.000-05:00</published><updated>2007-09-24T07:11:34.493-05:00</updated><title type='text'>Always pause for reflection</title><content type='html'>&lt;p&gt;I caught myself doing something rather foolish last night and I thought it would serve as a nice reminder for people who fall into the same trap I did.&lt;/p&gt;

&lt;p&gt;In short, I found myself rewriting library functions.  Stupid, I know, but sometimes you get caught up in a context that doesn't have you stopping to assess what it is you're doing.  I was profiling some different implementations of things and at one point I wrote&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;loop&lt;/span&gt; for elem in list
      thereis &lt;span class="paren"&gt;(&lt;/span&gt;funcall test check &lt;span class="paren"&gt;(&lt;/span&gt;funcall key elem&lt;span class="paren"&gt;)))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;instead of&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;find check list &lt;span class="builtin"&gt;:test&lt;/span&gt; test &lt;span class="builtin"&gt;:key&lt;/span&gt; key&lt;span class="paren"&gt;)&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;and I was wondering why the function it was in was a bit of a laggard.  (To be precise, the latter was between 6 and 7 times faster than the former!)  I had my head so buried in the problem that I couldn't see the forest for the trees.&lt;/p&gt;

&lt;p&gt;This is just further reinforcement for the notion that you should stand up every now and then and take stock of what it is you are doing.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-4312942664979092136?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/4312942664979092136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=4312942664979092136' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4312942664979092136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4312942664979092136'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/09/always-pause-for-reflection.html' title='Always pause for reflection'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-1476167482021382675</id><published>2007-09-16T19:49:00.000-05:00</published><updated>2007-09-16T20:18:49.383-05:00</updated><title type='text'>Fixing compiler macro annoyances in Allegro CL</title><content type='html'>&lt;p&gt;&lt;a href="http://franz.com/products/allegrocl/"&gt;Allegro CL&lt;/a&gt; is a nice Lisp implementation to use, but I recently discovered one caveat that was nearly a show-stopper for my work: compiler macros are not expanded for &lt;code&gt;setf&lt;/code&gt; functions.  This is &lt;a href="http://franz.com/support/documentation/8.1/ansicl/subsubsu/whencomp.htm"&gt;allowed by the standard&lt;/a&gt;, but is frustrating nevertheless.  As &lt;a href="http://groups.google.ca/group/comp.lang.lisp/msg/d881360ffa529a37"&gt;Willem Broekema&lt;/a&gt; pointed out, the lack of expansion of &lt;code&gt;setf&lt;/code&gt; compiler macros results in some weirdness when &lt;code&gt;setf&lt;/code&gt; is expanded and the place is a compound form that corresponds to a function with a compiler macro.&lt;/p&gt;

&lt;p&gt;I used the past tense above because I managed to come up with a way around this for my purposes.  Namely, I use &lt;code&gt;*macroexpand-hook*&lt;/code&gt; to inhibit or expand the compiler macro functions when in a compilation environment.  This is possible because ACL has environments support, which means it is possible to determine if a symbol names a compiler macro &lt;em&gt;and&lt;/em&gt; whether it is applicable.&lt;/p&gt;

&lt;p&gt;To inhibit the compiler macros, any compiler macro function that could cause problems is temporarily disabled when the &lt;code&gt;setf&lt;/code&gt; form is expanded.  This amounts to saving the compiler macro function and expanding the &lt;code&gt;setf&lt;/code&gt; form in an &lt;code&gt;unwind-protect&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Expanding the compiler macro function for the &lt;code&gt;setf&lt;/code&gt; function is more involved.  To properly expand the compiler macro, you would have to get the expansion of the &lt;code&gt;setf&lt;/code&gt; form and look for the form that makes the call to the &lt;code&gt;setf&lt;/code&gt; function.  Doing this properly requires a code walker.  I didn&amp;#8217;t want to bother with that since the situations I need to deal with are specific and I&amp;#8217;m not defining any custom &lt;code&gt;setf&lt;/code&gt; expanders.&lt;/p&gt;

&lt;p&gt;I took the following approach.  If the &lt;code&gt;setf&lt;/code&gt; form matches &lt;code&gt;(setf (foo ...) ...)&lt;/code&gt; (*) and there is a compiler macro function for &lt;code&gt;(setf foo)&lt;/code&gt; that can be applied, then disable compiler macros as above and get the expansion using &lt;code&gt;get-setf-expansion&lt;/code&gt;.  If the &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/05_aab.htm"&gt;storing form&lt;/a&gt; matches &lt;code&gt;(funcall #'(setf foo) ...)&lt;/code&gt; then expand the compiler macro for &lt;code&gt;(setf foo)&lt;/code&gt; and use the form it returns in place of the storing form.&lt;/p&gt;

&lt;p&gt;This solution is not suitable for all situations since it doesn&amp;#8217;t expand forms in the correct order.  Specifically, it doesn&amp;#8217;t expand any forms that evaluate to the arguments passed to &lt;code&gt;(setf foo)&lt;/code&gt; before getting its compiler macro expansion (these are returned by &lt;code&gt;get-setf-expansion&lt;/code&gt;).  I also didn&amp;#8217;t bother with &lt;code&gt;psetf&lt;/code&gt;, but that&amp;#8217;s only because I didn&amp;#8217;t need it.&lt;/p&gt;

&lt;p&gt;I thought of two other ways that you could go about expanding &lt;code&gt;setf&lt;/code&gt; compiler macros, but I haven&amp;#8217;t thought the approaches through in great detail.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If &lt;code&gt;setf&lt;/code&gt; calls are always of the form &lt;code&gt;(funcall #'(setf foo) ...)&lt;/code&gt; then you could put a compiler macro function on &lt;code&gt;funcall&lt;/code&gt; and call the appropriate compiler macro.  I played with this a little, but ACL defines a compiler macro for &lt;code&gt;funcall&lt;/code&gt; already and I&amp;#8217;m not sure what it does.&lt;/li&gt;
&lt;li&gt;You could define a &lt;code&gt;setf&lt;/code&gt; expander to return a different form when in a compilation environment.  I&amp;#8217;m not convinced this is markedly different than the above approach and it would probably involve more work or be less aesthetically pleasing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Code for this is available &lt;a href="http://wozniak.ca/projects/setf-compiler-macros/setf-cmf.lisp"&gt;here&lt;/a&gt; with a &lt;a href="http://wozniak.ca/projects/setf-compiler-macros/setf-cmf.html"&gt;colourized html version&lt;/a&gt; as well.  The interface needs work, but it is suitable for my purposes.&lt;/p&gt;

&lt;p&gt;(*) Note that &lt;code&gt;(setf p1 v1 p2 v2 ...)&lt;/code&gt; is transformed into the equivalent &lt;code&gt;(progn (setf p1 v1) (setf p2 v2) ...)&lt;/code&gt; before looking for this pattern.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-1476167482021382675?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/1476167482021382675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=1476167482021382675' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1476167482021382675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1476167482021382675'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/09/fixing-compiler-macro-annoyances-in.html' title='Fixing compiler macro annoyances in Allegro CL'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-4085636944611539750</id><published>2007-08-19T09:42:00.000-05:00</published><updated>2007-08-19T10:12:52.384-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='macros'/><title type='text'>Lisp quiz of the day: macroexpansion gotchas</title><content type='html'>&lt;p&gt;Suppose you want to use *MACROEXPAND-HOOK* to track something while debugging.  For example, you're interested in SETF forms with the pattern (SETF (FOO ...) ...).  You might decide to try something like the following.  (The values returned by TRACKING-HOOK are just for demonstration.)&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;setf-list-form-p&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;form&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;and &lt;span class="paren"&gt;(&lt;/span&gt;consp form&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;eq 'setf &lt;span class="paren"&gt;(&lt;/span&gt;car form&lt;span class="paren"&gt;))&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;consp &lt;span class="paren"&gt;(&lt;/span&gt;cadr form&lt;span class="paren"&gt;))))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;setf-form-place-name&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;form&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;setf-list-form-p form&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;caadr form&lt;span class="paren"&gt;)))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;tracking-hook&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;expander form env&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;eql 'foo &lt;span class="paren"&gt;(&lt;/span&gt;setf-form-place-name form&lt;span class="paren"&gt;))&lt;/span&gt;
      &lt;span class="paren"&gt;(&lt;/span&gt;values '&lt;span class="paren"&gt;(&lt;/span&gt;cons &lt;span class="string"&gt;"Yes"&lt;/span&gt; nil&lt;span class="paren"&gt;)&lt;/span&gt; t&lt;span class="paren"&gt;)&lt;/span&gt;
      &lt;span class="paren"&gt;(&lt;/span&gt;values '&lt;span class="paren"&gt;(&lt;/span&gt;cons &lt;span class="string"&gt;"No"&lt;/span&gt; nil&lt;span class="paren"&gt;)&lt;/span&gt; t&lt;span class="paren"&gt;)))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;If you're not careful (see below) and use this directly in Allegro CL, you will have a problem.  (Try to figure out why before reading on.)&lt;/p&gt;

&lt;p&gt;To make things a little easier, try it out and see what happens.  Assume we have entered the definitions for the above functions are the REPL already.&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(let ((*macroexpand-hook* 'tracking-hook))
           (macroexpand-1 '(setf (foo 10) 10)))&lt;/span&gt;
Error: Stack overflow (signal 1000)
  [condition type: SYNCHRONOUS-OPERATING-SYSTEM-SIGNAL]
&lt;/pre&gt;

&lt;p&gt;So, why does this happen?&lt;/p&gt;

&lt;p&gt;This had me stumped for a while this morning until I looked at some stack traces by using BREAK at the beginning of TRACKING-HOOK.  The problem will go away if you compile SETF-LIST-FORM-P and SETF-FORM-PLACE-NAME.&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(compile 'setf-list-form-p)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;SETF-LIST-FORM-P&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(compile 'setf-form-place-name)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;SETF-FORM-PLACE-NAME&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(let ((*macroexpand-hook* 'tracking-hook))
           (macroexpand-1 '(setf (foo 10) 10)))&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;(CONS "Yes" NIL)&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;T&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;The problem lies in how the function is evaluated.  Each call to TRACKING-HOOK results in the body of SETF-FORM-PLACE-NAME being evaluated, which results in a macroexpansion of the WHEN form.  This means that TRACKING-HOOK gets called again!&lt;/p&gt;

&lt;p&gt;Welcome to infinite recursion land.&lt;/p&gt;

&lt;p&gt;Compiling will steer us away from this because no macroexpansion happens for the evaluation of a compiled function.&lt;/p&gt;

&lt;p&gt;The same behaviour can be seen in LispWorks Personal 5.0.1, but not CLisp 2.4 or SBCL 1.0.8.27.  SBCL wouldn't let me bind *MACROEXPAND-HOOK* to a non-compiled function.  CLisp worked, but when I tried to insert a BREAK form in TRACKING-HOOK or SETF-FORM-PLACE-NAME, it would print out information on the break some number of times and start using all the CPU.  I'm not sure why yet.&lt;/p&gt;

&lt;p&gt;The moral of the story: compile functions to be called from macroexpansion hooks.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-4085636944611539750?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/4085636944611539750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=4085636944611539750' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4085636944611539750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/4085636944611539750'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/08/lisp-quiz-of-day-macroexpansion-gotchas.html' title='Lisp quiz of the day: macroexpansion gotchas'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5067767957804324634</id><published>2007-08-16T08:34:00.001-05:00</published><updated>2007-08-16T10:20:21.342-05:00</updated><title type='text'>Grandstanding on a tar pit</title><content type='html'>&lt;p&gt;Last night I read a transcription of a panel discussion from a &lt;a href="http://alpha.lib.uwo.ca/search/?searchtype=c&amp;searcharg=QA76.7.R65&amp;searchscope=13&amp;search.x=16&amp;search.y=11&amp;SORT=A"&gt;symposium on the role of language in problem solving&lt;/a&gt; wherein I found the following, care of &lt;a href="http://www.cs.umd.edu/~ben/"&gt;Ben Schneiderman&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was very strongly influenced by an article by John Platt in Science magazine in 1964 &lt;a href="http://links.jstor.org/sici?sici=0036-8075%2819641016%293%3A146%3A3642%3C347%3ASI%3E2.0.CO%3B2-K"&gt;(October 10, p. 347)&lt;/a&gt; [subscription required]. He relates the history of primary disciplines in the 20th century, molecular biology and particle physics. He recounts how the laboratories of researchers would have on their bulletin boards or blackboards tree structures of experiments. The outcome of each experiment would determine the next experiment--very clear delineation of research plans. Yet, much of the work I see today is just an exploration, without a formal basis, without a rigorous underpinning, without a theory of human behavior, and without a commitment to an empirical approach which is based on human factors studies about how people really use different systems. I think once we move in that direction and get beyond the arguments about "my language is more user friendly than your language" and "my system is more natural than your system" &amp;mdash whenever you hear the speakers in the next two days saying "better", "natural", "more understandable", try to ask them what they mean, what are the metrics &amp;mdash; and only when we get to measurement do we begin to really talk about science.&lt;/p&gt;


&lt;p&gt;[...] I think we risk being perceived by not only the general population but our colleagues and other scientific disciplines as being so much the snake oil salesmen. We may discover some tool or idea which works well for one domain, maybe it cures some people of headaches sometimes, but then we promote it as if it's the cure-all for everything. I hope for a much more rigorous and diligent approach to address what... [was] referred to as the multiplicity of tasks that users have to accomplish and the diversity of users. For me that's been the greatest encounter in becoming 20% of an experimental psychologist, in discovering how very differently individuals behave from each other and how very different they are from myself. It's a great struggle to overcome the egocentric approach that many people take towards design and to open our minds, to find out how other people work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I found that particularly poignant given the postings in a thread in comp.lang.lisp lately (which, mercifully, seem to be subsiding).  Consider what Don Geddis &lt;a href="http://groups.google.ca/group/comp.lang.lisp/msg/8348dfd30730f099"&gt;has to say&lt;/a&gt; about it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This whole thread might have been short circuited if the static typing fans had merely said: "static/dynamic typing is a tradeoff, with the following pros and cons, and we are part of the programming community that prefers the static side of the tradeoff." &lt;/p&gt;

&lt;p&gt;Instead of: "static typing is the future; all modern languages use it; languages that use dynamic typing are relics of the past, soon to be extinct or only used for toy scripts.  But no educated, serious, mature programmer could possibly make the tradeoff that the benefits of dynamic typing are worth the cost of losing the static type checker.  Only ignorant, uneducated, naive programmers believe such primitive nonsense." &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Inevitably, when a thread balloons to over 1000 messages, it becomes a Möbius strip of conversation: You get the occasional twist, but you really aren't going anywhere.)&lt;/p&gt;

&lt;p&gt;The aforementioned thread suffers from what Schneiderman is talking about and shows why the static/dynamic typing question is worthless discussion.  People on both "sides" (or just the vocal people?) make grandiose claims with no evidence to back it up, mostly because there is little to none of it in the first place.&lt;/p&gt;

&lt;p&gt;My personal experience shows me that a &lt;em&gt;lack&lt;/em&gt; of statically checked, strong typing has benefits in terms of program evolution, development and problem solving.  Conversely, I've found that rigorous checks can be very useful when I'm clear on what it is I want to say.  I cannot (and will not, although I might have when I was more ignorant than I am now) make claims about one being inherently better than the other because the metrics behind such a statement are (still) unclear.  And really, my best evidence is anecdotal.  Meta-analysis of what goes on in industry is the best evidence I've seen so far, and even then it's awfully wishy-washy.&lt;/p&gt;

&lt;p&gt;For those who want do their grandstanding on a tar pit, go right ahead.  People with sense and humility know that you aren't really saying much.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5067767957804324634?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5067767957804324634/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5067767957804324634' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5067767957804324634'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5067767957804324634'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/08/grandstanding-on-tar-pit.html' title='Grandstanding on a tar pit'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-8805061265232088237</id><published>2007-08-15T18:50:00.000-05:00</published><updated>2007-08-15T22:02:18.360-05:00</updated><title type='text'>Amazon taketh away, then giveth back a little</title><content type='html'>&lt;p&gt;Amazon's decision to summarily cancel the order of Lisp in Small Pieces and the other tech books priced at $3.95 CDN irked me a little.  Yeah, it seemed too good to be true, so I guess I wasn't shocked that it was "displayed at the incorrect price", as stated in their rejection letter.  Still, I have a hard time believing that red flags weren't raised sooner than they were.  At the very least, someone with some influence at Amazon must have heard about the story from &lt;a href="http://programming.reddit.com/info/2dol4/comments"&gt;Reddit&lt;/a&gt; or &lt;a href="http://digg.com/programming/A_Book_On_Lisp_Beats_Harry_Potter_On_The_Bestseller_List"&gt;Digg&lt;/a&gt;.  Maybe they didn't care to say anything.&lt;/p&gt;

&lt;p&gt;At least Amazon was nice enough to include $10 gift certificates with the notification that my books had been cancelled &amp;mdash well after I noticed they were removed from my order, of course.  I used those to get The LaTeX Companion, but I expect that will the be last book I buy from them for a little while.&lt;/p&gt;

&lt;p&gt;Their policy is quite clear on this, but that doesn't make the taste in my mouth any less bitter.  Methinks I'll be cruising &lt;a href="http://www.abebooks.com/"&gt;AbeBooks&lt;/a&gt; for my next purchases...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-8805061265232088237?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/8805061265232088237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=8805061265232088237' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/8805061265232088237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/8805061265232088237'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/08/amazon-taketh-away-then-giveth-back.html' title='Amazon taketh away, then giveth back a little'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-1993838636934355451</id><published>2007-08-09T23:12:00.000-05:00</published><updated>2007-08-09T23:20:09.262-05:00</updated><title type='text'>Lisp In Small Pieces craze may be running out of gas</title><content type='html'>&lt;p&gt;It looks like the &lt;em&gt;Lisp In Small Pieces&lt;/em&gt; for $3.95 craze has met its end.  The book is no longer listed with a price, nor is it listed as available, except from other sellers.&lt;/p&gt;

&lt;a href="http://3.bp.blogspot.com/_StomEfmr9Cc/RrvmuEUJQwI/AAAAAAAAAAg/aGSFZaSUE30/s1600-h/LiSP+not+so+available.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_StomEfmr9Cc/RrvmuEUJQwI/AAAAAAAAAAg/aGSFZaSUE30/s320/LiSP+not+so+available.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5096921082406454018" /&gt;&lt;/a&gt;

&lt;p&gt;I ordered my copy yesterday.  Here's hoping there aren't any problems with it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-1993838636934355451?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/1993838636934355451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=1993838636934355451' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1993838636934355451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1993838636934355451'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/08/lisp-in-small-pieces-craze-may-be.html' title='Lisp In Small Pieces craze may be running out of gas'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_StomEfmr9Cc/RrvmuEUJQwI/AAAAAAAAAAg/aGSFZaSUE30/s72-c/LiSP+not+so+available.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5130015308368693685</id><published>2007-08-05T22:11:00.000-05:00</published><updated>2007-08-05T22:55:24.131-05:00</updated><title type='text'>Reflecting on development and types</title><content type='html'>&lt;p&gt;After teaching a course on algorithm analysis and design for over a month and having it consume most of my time, I've come back to a flurry of words that gives the impression of a discussion in &lt;a href="http://groups.google.ca/group/comp.lang.lisp/browse_thread/thread/7b1ab36f5d5cce0a?tvc=2"&gt;comp.lang.lisp and comp.lang.functional&lt;/a&gt;. It has given me pause to reflect on how I approach solving my coding problems.  Specifically, this &lt;a href="http://groups.google.ca/group/comp.lang.lisp/msg/73cbfee90307c752"&gt;paragraph&lt;/a&gt; from Pascal Costanza caught my attention.&lt;/p&gt;

&lt;blockquote&gt;
If I understand correctly, people who prefer static type systems tend to think in terms of structural invariants of their programs, while people who prefer dynamic type systems tend to think in terms of their programs' behavior. I am convinced this is a personal preference, nothing more, nothing less. I guess that static typers have equal problems developing in dynamically typed languages as dynamic typers have developing in statically typed languages. I don't see a problem here: If the requirements are such that a focus on static correctness is important, it's probably better to employ a static typer, whereas if the requirements are such that dynamic flexibility is more important, the job should better be done by a dynamic typer. There is nothing wrong with specialization, we don't need a grand unified theory of program development. 
&lt;/blockquote&gt;

&lt;p&gt;My recent work has me experimenting with ways to profile data processed by programs then analyzing that data to see how it can be used to optimize those programs.  Additionally, I'm exploring ways to measure the specificity (an admittedly vague term) of programs.  During this time, it's been tough to ascertain just what it is I want to collect data about and how it should be stored.  In the process of figuring things out, I tend to throw information into lists and hash tables just so I can take a look at it later knowing full well that I don't really know what I'm going to be dealing with.  Hence it is difficult, if not impossible, for me to specify much ahead of time.&lt;/p&gt;

&lt;p&gt;As things become clearer, I come up with more specific types and data stores within an already working framework.  In short, the program I'm writing helps me to figure out how to make the program “more specific” in some sense (say, for example, I know the kinds of things that will be put into a list).  But at this point, things are behaving as required and the specific types are icing on the cake.&lt;/p&gt;

&lt;p&gt;I switched to using Lisp for my work because it fit my problem solving methodology.  I started out using OCaml and appreciated the language, but I didn't really enjoy writing in it; it didn't mesh with my approach to the problem.  Is this the fault of the language?  Hardly, and I would never make the claim that it was.  It's also silly to claim that this approach is impossible in OCaml.  I just didn't like it.&lt;/p&gt;

&lt;p&gt;My personal experience suggests that Pascal's comment is accurate.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5130015308368693685?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5130015308368693685/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5130015308368693685' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5130015308368693685'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5130015308368693685'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/08/reflecting-on-development-and-types.html' title='Reflecting on development and types'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-3782290800589868349</id><published>2007-06-25T18:22:00.000-05:00</published><updated>2007-06-27T08:45:38.016-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='speed test'/><category scheme='http://www.blogger.com/atom/ns#' term='slot access'/><title type='text'>Investigating slot access</title><content type='html'>&lt;p&gt;My curiosity has a way of making me check some seemingly innocuous things. I've been thinking about implementations of maps and associative arrays. Besides hash tables and association lists, structures and classes can act as maps. This got me wondering about the speed of slot access, where a "slot" is defined as a mapping of one element to another. In a hash table or association list, this is the association of an element with a key. In structures and instances (where "instance" means a direct instance of a class defined with &lt;span style="font-family: monospace;"&gt;defclass&lt;/span&gt;), it's the usual definition of labelling a particular element that will be accessed frequently.&lt;/p&gt;

  &lt;p&gt;I wrote a test to see the difference between these different map implementations across multiple Lisp implementations. The code I used to test access times is given below. The access test consists of setting the slot to a random number, then reading the slot and detecting some trivial condition. I hypothesized that slot access in structure instances would be slightly faster than other instances due to the lack of generic function overhead. Indirect access to class instances using &lt;span style="font-family: monospace;"&gt;slot-value&lt;/span&gt; would probably be similar to hash tables, which would be considerably slower than direct access.&lt;/p&gt;
  &lt;pre class="code"&gt;
&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defstruct&lt;/span&gt; &lt;span class="type"&gt;struct-example&lt;/span&gt; f0&lt;span class="paren"&gt;)&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;class-example&lt;/span&gt; &lt;span class="paren"&gt;()&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;f0 &lt;span class="builtin"&gt;:accessor&lt;/span&gt; class-example-f0&lt;span class="paren"&gt;)))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;run-access-test&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;creator-form accessor-form times&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;let*&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;example &lt;span class="paren"&gt;(&lt;/span&gt;gensym &lt;span class="string"&gt;"EXAMPLE-"&lt;/span&gt;&lt;span class="paren"&gt;))&lt;/span&gt;
         &lt;span class="paren"&gt;(&lt;/span&gt;aform &lt;span class="paren"&gt;(&lt;/span&gt;substitute example '* accessor-form&lt;span class="paren"&gt;)))&lt;/span&gt;
    `&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;let&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;,example ,creator-form&lt;span class="paren"&gt;))&lt;/span&gt;
       &lt;span class="paren"&gt;(&lt;/span&gt;format t &lt;span class="string"&gt;"~%Testing ~S with ~S~%"&lt;/span&gt;
               &lt;span class="paren"&gt;(&lt;/span&gt;quote ,creator-form&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;quote ,accessor-form&lt;span class="paren"&gt;))&lt;/span&gt;
       &lt;span class="paren"&gt;(&lt;/span&gt;time
        &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;loop&lt;/span&gt;
           repeat ,times
           do &lt;span class="paren"&gt;(&lt;/span&gt;setf ,aform &lt;span class="paren"&gt;(&lt;/span&gt;random 100&lt;span class="paren"&gt;))&lt;/span&gt;
           counting &lt;span class="paren"&gt;(&lt;/span&gt;= 0 &lt;span class="paren"&gt;(&lt;/span&gt;mod ,aform 13&lt;span class="paren"&gt;)))))))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;run-tests&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;times&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;run-access-test
   &lt;span class="paren"&gt;(&lt;/span&gt;make-struct-example&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;struct-example-f0 *&lt;span class="paren"&gt;)&lt;/span&gt; times&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;run-access-test
   &lt;span class="paren"&gt;(&lt;/span&gt;make-instance 'class-example&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;class-example-f0 *&lt;span class="paren"&gt;)&lt;/span&gt; times&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;let&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;slot 'f0&lt;span class="paren"&gt;))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;run-access-test
     &lt;span class="paren"&gt;(&lt;/span&gt;make-instance 'class-example&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;slot-value * slot&lt;span class="paren"&gt;)&lt;/span&gt; times&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;run-access-test
   &lt;span class="paren"&gt;(&lt;/span&gt;make-hash-table &lt;span class="builtin"&gt;:test&lt;/span&gt; 'eq&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;gethash 'f0 *&lt;span class="paren"&gt;)&lt;/span&gt; times&lt;span class="paren"&gt;))&lt;/span&gt;
&lt;/pre&gt;

  &lt;p&gt;I compiled the code with default compiler settings and executed &lt;span style="font-family: monospace;"&gt;(run-tests (expt 10 7))&lt;/span&gt;, recording the times, not including any system time or garbage collection. The results are given in the graph and table below. To my surprise, things didn't work in quite the fashion I was expecting. The difference between direct and indirect access was narrower than I thought and there was one case where my hypothesis did not hold up.&lt;/p&gt;

&lt;a href="http://1.bp.blogspot.com/_StomEfmr9Cc/RoJpZHQgCKI/AAAAAAAAAAY/ikovI4Ira5A/s1600-h/Access+times.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_StomEfmr9Cc/RoJpZHQgCKI/AAAAAAAAAAY/ikovI4Ira5A/s400/Access+times.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5080739209793243298" /&gt;&lt;/a&gt;

  &lt;div align="center"&gt;
    &lt;table class="data"&gt;
      &lt;caption&gt;
        Running times reported by &lt;span style="font-family: monospace"&gt;(run-tests (expt 10 7))&lt;/span&gt; in seconds.
      &lt;/caption&gt;

      &lt;thead&gt;
        &lt;tr&gt;
          &lt;th&gt;Access type&lt;/th&gt;

          &lt;td&gt;SBCL&lt;/td&gt;

          &lt;td&gt;Allegro&lt;/td&gt;

          &lt;td&gt;LispWorks&lt;/td&gt;

          &lt;td&gt;CLISP&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;

      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;th&gt;Hash tables&lt;/th&gt;

          &lt;td&gt;3.131&lt;/td&gt;

          &lt;td&gt;2.560&lt;/td&gt;

          &lt;td&gt;2.884&lt;/td&gt;

          &lt;td&gt;7.880&lt;/td&gt;
        &lt;/tr&gt;

        &lt;tr&gt;
          &lt;th&gt;Indirect (using &lt;span style="font-family: monospace;"&gt;slot-value&lt;/span&gt;)&lt;/th&gt;

          &lt;td&gt;3.079&lt;/td&gt;

          &lt;td&gt;2.019&lt;/td&gt;

          &lt;td&gt;2.984&lt;/td&gt;

          &lt;td&gt;7.541&lt;/td&gt;
        &lt;/tr&gt;

        &lt;tr&gt;
          &lt;th&gt;Direct (using &lt;span style="font-family: monospace;"&gt;:accessor&lt;/span&gt;)&lt;/th&gt;

          &lt;td&gt;0.837&lt;/td&gt;

          &lt;td&gt;1.530&lt;/td&gt;

          &lt;td&gt;2.240&lt;/td&gt;

          &lt;td&gt;12.423&lt;/td&gt;
        &lt;/tr&gt;

        &lt;tr&gt;
          &lt;th&gt;Structure&lt;/th&gt;

          &lt;td&gt;0.624&lt;/td&gt;

          &lt;td&gt;1.200&lt;/td&gt;

          &lt;td&gt;1.586&lt;/td&gt;

          &lt;td&gt;6.927&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;

  &lt;p&gt;The first thing to notice is that CLISP has awful direct access times on my setup. I ran the test several times just to be sure the result wasn't an outlier — it wasn't. The difference between direct access in instances versus structures is similar across implementations, save for CLISP; each differ by a factor of around 1.3 or 1.4 (CLISP is about 1.8). However, the difference in indirect access versus direct access varies from a factor of 3.7 for SBCL to about 1.3 for both Allegro and LispWorks. Again, CLISP is the freak in that &lt;em&gt;indirect&lt;/em&gt; access is 1.6 times faster than direct access.&lt;/p&gt;

  &lt;p&gt;Part of the reason I'm looking into this is that I'm looking at automatic generation of representations for maps. This simple test gives me an idea as to what I should be factoring into my decisions.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-3782290800589868349?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/3782290800589868349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=3782290800589868349' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3782290800589868349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/3782290800589868349'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/06/investigating-slot-access.html' title='Investigating slot access'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_StomEfmr9Cc/RoJpZHQgCKI/AAAAAAAAAAY/ikovI4Ira5A/s72-c/Access+times.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-808585195972780207</id><published>2007-06-03T11:22:00.001-05:00</published><updated>2007-06-03T11:22:15.261-05:00</updated><title type='text'>In defense of LOOP</title><content type='html'>&lt;p&gt;
  &lt;a href="http://eval.apply.googlepages.com/home"&gt;Joe Marshall&lt;/a&gt; gives some &lt;a href="http://funcall.blogspot.com/2007/06/why-i-am-knee-jerk-anti-loopist.html"&gt;reasons to hate LOOP&lt;/a&gt; and I can't say I disagree with him, save for has last point.
&lt;/p&gt;

&lt;blockquote&gt;
  LOOP encourages users to think linearly, sequentially, and iteratively. They start trying to flatten everything into a single pass, first-to-last, state-bashing mess. Suddenly they forget about recursion. I've seen this happen over and over again. It literally damages the thought process.
&lt;/blockquote&gt;

&lt;p&gt;
  This alludes to the old &lt;a href="http://www.google.com/search?ie=utf8&amp;amp;oe=utf8&amp;amp;q=iteration+versus+recursion"&gt;iteration versus recursion&lt;/a&gt; quagmire which is analogous to &lt;a href="http://c2.com/cgi/wiki?StaticVsDynamicTyping"&gt;static versus dynamic&lt;/a&gt; &amp;quot;debates.&amp;quot;  Joe's self-professed knee-jerk reaction isn't going to get me to stop me from using &lt;span style="font-family: monospace;"&gt;LOOP&lt;/span&gt;, nor will it convince me to use recursion more.  With both, I will use the one that fits the problem best given the context in which it is written.  &lt;/p&gt;

&lt;p&gt;
  Mapping over hash tables is one such example.  Suppose I have a hash table whose values are lists and I want to calculate the sum of the length of all the lists.  &lt;span style="font-family: monospace;"&gt;LOOP&lt;/span&gt; provides a succinct and clear way to express it.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;loop&lt;/span&gt; for list being each hash-value in htable
      sum &lt;span class="paren"&gt;(&lt;/span&gt;length list&lt;span class="paren"&gt;))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  The &lt;span style="font-family: monospace;"&gt;MAPHASH&lt;/span&gt; and &lt;span style="font-family: monospace;"&gt;WITH-HASH-TABLE-ITERATOR&lt;/span&gt; approaches aren't quite as simple (although, admittedly, not difficult for this problem).
&lt;/p&gt;

&lt;p&gt;
  &lt;span style="font-family: monospace;"&gt;LOOP&lt;/span&gt; is also really handy for short-circuiting.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;first-n-things&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;list pred n&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;loop&lt;/span&gt; with count = 0
        for e in list
        when &lt;span class="paren"&gt;(&lt;/span&gt;funcall pred e&lt;span class="paren"&gt;)&lt;/span&gt; collect e and do &lt;span class="paren"&gt;(&lt;/span&gt;incf count&lt;span class="paren"&gt;)&lt;/span&gt;
        until &lt;span class="paren"&gt;(&lt;/span&gt;= count n&lt;span class="paren"&gt;)))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  I can't say I'm fond of the &lt;span style="font-family: monospace;"&gt;LABELS&lt;/span&gt; approach to the latter.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;first-n-things-2&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;list pred n&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;labels&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;helper &lt;span class="paren"&gt;(&lt;/span&gt;elems count acc&lt;span class="paren"&gt;)&lt;/span&gt;
             &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;cond&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;or &lt;span class="paren"&gt;(&lt;/span&gt;null elems&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;= n count&lt;span class="paren"&gt;))&lt;/span&gt;
                    acc&lt;span class="paren"&gt;)&lt;/span&gt;
                   &lt;span class="paren"&gt;((&lt;/span&gt;funcall pred &lt;span class="paren"&gt;(&lt;/span&gt;first elems&lt;span class="paren"&gt;))&lt;/span&gt;
                    &lt;span class="paren"&gt;(&lt;/span&gt;helper &lt;span class="paren"&gt;(&lt;/span&gt;rest elems&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;1+ count&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;cons &lt;span class="paren"&gt;(&lt;/span&gt;first elems&lt;span class="paren"&gt;)&lt;/span&gt; acc&lt;span class="paren"&gt;)))&lt;/span&gt;
                   &lt;span class="paren"&gt;(&lt;/span&gt;t &lt;span class="paren"&gt;(&lt;/span&gt;helper &lt;span class="paren"&gt;(&lt;/span&gt;rest elems&lt;span class="paren"&gt;)&lt;/span&gt; count acc&lt;span class="paren"&gt;)))))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;helper list 0 nil&lt;span class="paren"&gt;)))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  Using an &lt;span style="font-family: monospace;"&gt;&amp;amp;OPTIONAL&lt;/span&gt; parameter as an accumulator is only slightly cleaner.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;first-n-things-3&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;list pred n &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; acc&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;cond&lt;/span&gt;
    &lt;span class="paren"&gt;((&lt;/span&gt;or &lt;span class="paren"&gt;(&lt;/span&gt;null list&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;zerop n&lt;span class="paren"&gt;))&lt;/span&gt;
     acc&lt;span class="paren"&gt;)&lt;/span&gt;
    &lt;span class="paren"&gt;((&lt;/span&gt;funcall pred &lt;span class="paren"&gt;(&lt;/span&gt;first list&lt;span class="paren"&gt;))&lt;/span&gt;
     &lt;span class="paren"&gt;(&lt;/span&gt;first-n-things-3 &lt;span class="paren"&gt;(&lt;/span&gt;rest list&lt;span class="paren"&gt;)&lt;/span&gt; pred &lt;span class="paren"&gt;(&lt;/span&gt;1- n&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;cons &lt;span class="paren"&gt;(&lt;/span&gt;first list&lt;span class="paren"&gt;)&lt;/span&gt; acc&lt;span class="paren"&gt;)))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;t &lt;span class="paren"&gt;(&lt;/span&gt;first-n-things-3 &lt;span class="paren"&gt;(&lt;/span&gt;rest list&lt;span class="paren"&gt;)&lt;/span&gt; pred n acc&lt;span class="paren"&gt;))))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
  Debunking the &amp;quot;&lt;span style="font-family: monospace;"&gt;LOOP&lt;/span&gt; damages the thought process&amp;quot; claim isn't the point here, since I don't think it was meant to be an assertion in the first place.  LOOP makes dealing with state a lot cleaner when it comes up, but I agree that there is no need to use it when a simple &lt;span style="font-family: monospace;"&gt;MAPCAR&lt;/span&gt; will do.  Sometimes recursion is messy, sometimes it isn't.  Frankly, I go back forth between the two all the time, trying to find the best way to express my problem, just like I do when I'm writing.  I don't always succeed, but I want to keep my options open.
&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-808585195972780207?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/808585195972780207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=808585195972780207' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/808585195972780207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/808585195972780207'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/06/in-defense-of-loop.html' title='In defense of LOOP'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-6255012104625836097</id><published>2007-05-30T13:55:00.001-05:00</published><updated>2007-05-30T17:30:38.942-05:00</updated><title type='text'>Objects as functions</title><content type='html'>&lt;p&gt;
Normally you can't apply a non-function object to another object and get a useful result (unless the error it produces is meaningful).  You have to define functions that work on objects to do something with them.  Sometimes this is annoying &amp;mdash; especially when the mapping that the function represents is not fixed.
&lt;/p&gt;

&lt;p&gt;
The programming language &lt;a href="http://cs1.cs.nyu.edu/bacon/download-setl.html"&gt;SETL&lt;/a&gt; has something called &lt;em&gt;maps&lt;/em&gt;, which are sets that contained ordered pairs.  A map can be applied to an object and if the object is the first element of an ordered pair, the second element (or set of them) is returned.  What's neat about SETL maps is that they can be extended easily.
&lt;/p&gt;

&lt;pre class="code"&gt;$ Welcome to example program land!
f := {[1, 100], [2, 200]};   $ define f
print(f(1));
print(f(2));
f(3) := 300;                 $ add the pair [3, 300] to f
print(f(3));
&lt;/pre&gt;

&lt;pre class="code"&gt;
[woz ~] $ setl -k example.setl       # no REPL... (*sigh*)
100
200
300
&lt;/pre&gt;

&lt;p&gt;
There are a couple ways to add this kind of behaviour to Common Lisp, but the cleanest I've found uses CLOS and the MOP.
&lt;/p&gt;

&lt;p&gt;
The MOP defines &lt;a href="http://www.lisp.org/mop/concepts.html#funcallable-instances"&gt;funcallable instances&lt;/a&gt; which allow objects to be called as functions.  From the example given in the documentation, it's straightforward to build an object that acts like a map.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;asdf:operate 'asdf:load-op &lt;span class="builtin"&gt;:closer-mop&lt;/span&gt;&lt;span class="paren"&gt;)&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;setl-map&lt;/span&gt; &lt;span class="paren"&gt;()&lt;/span&gt;
  &lt;span class="paren"&gt;((&lt;/span&gt;pairs &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:pairs&lt;/span&gt; &lt;span class="builtin"&gt;:initform&lt;/span&gt; nil &lt;span class="builtin"&gt;:accessor&lt;/span&gt; setl-map-pairs&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="builtin"&gt;:metaclass&lt;/span&gt; closer-mop:funcallable-standard-class&lt;span class="paren"&gt;))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;initialize-instance&lt;/span&gt; &lt;span class="builtin"&gt;:after&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;instance setl-map&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; initargs&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;declare&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;ignore initargs&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;closer-mop:set-funcallable-instance-function
   instance
   #'&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;lambda&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;x&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;rest &lt;span class="paren"&gt;(&lt;/span&gt;assoc x &lt;span class="paren"&gt;(&lt;/span&gt;setl-map-pairs instance&lt;span class="paren"&gt;))))))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
We can now go about making a function f that will act like the one from SETL.  In order to add pairs, we'll define a &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/05_abi.htm"&gt;setf function&lt;/a&gt;.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;f&lt;/span&gt; &lt;span class="paren"&gt;()&lt;/span&gt; nil&lt;span class="paren"&gt;)&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;setf &lt;span class="paren"&gt;(&lt;/span&gt;symbol-function 'f&lt;span class="paren"&gt;)&lt;/span&gt;
      &lt;span class="paren"&gt;(&lt;/span&gt;make-instance 'setl-map &lt;span class="builtin"&gt;:pairs&lt;/span&gt; '&lt;span class="paren"&gt;((&lt;/span&gt;1 . 100&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;2 . 200&lt;span class="paren"&gt;))))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;setf f&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;result arg&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;let&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;setl-map &lt;span class="paren"&gt;(&lt;/span&gt;symbol-function 'f&lt;span class="paren"&gt;)))&lt;/span&gt;
    &lt;span class="paren"&gt;(&lt;/span&gt;push &lt;span class="paren"&gt;(&lt;/span&gt;cons arg result&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;setl-map-pairs setl-map&lt;span class="paren"&gt;))&lt;/span&gt;
    result&lt;span class="paren"&gt;))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
Now f is a function that we can call and add things to.
&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(f 1)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;100&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(f 2)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;200&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(setf (f 3) 300)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;300&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(f 3)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;300&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
Implementing the definition of f (and its setf version) as a macro and adding the ability to remove pairs is left as an exercise to the reader.
&lt;/p&gt;

&lt;p&gt;
Funcallable objects come in handy when you want (or need) to use a function as the interface to an object or a collection of objects.  I generally use them when I need to define a mapping that changes over time and holding the mapping outside of the function would be cumbersome.  They're a handy tool to have around.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-6255012104625836097?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/6255012104625836097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=6255012104625836097' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/6255012104625836097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/6255012104625836097'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/05/objects-as-functions.html' title='Objects as functions'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5628552786429674762</id><published>2007-05-23T06:37:00.001-05:00</published><updated>2007-05-23T06:37:29.898-05:00</updated><title type='text'>Using data profiling in a running system</title><content type='html'>&lt;div class="executive-summary"&gt;
&lt;p&gt;
Profiling a program usually means profiling function calls.  Why not profile data?  Furthermore, what can be gained by making the profiling data available at run time?  I attempt to show that using profiling data at run time allows you to  create adaptable data objects with, dare I say, interesting results.  I also introduce some the stuff I've been working on of late.
&lt;/p&gt;

&lt;p&gt;
(Note: This is a long entry.)
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
According to Wirth, &lt;a href="http://www.amazon.com/Algorithms-Structures-Prentice-Hall-Automatic-Computation/dp/0130224189"&gt;programs equal algorithms plus data structures&lt;/a&gt;.  Profiling programs mostly consists of watching how functions behave &amp;mdash; and functions roughly correspond to algorithms.  Given the equation, why not profile data structures as well?
&lt;/p&gt;

&lt;p&gt;
The concept is not foreign.  Watching data is a common occurrence when debugging in order to verify its structural integrity and the correctness of any related algorithms.  This is part of the impetus behind &lt;a href="http://sourceware.org/gdb/current/onlinedocs/gdb_6.html#SEC34"&gt;watchpoints&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Data profiling is also done when selecting a structure to hold data.  Developers make trade-offs in choosing a representation; this is part and parcel of program creation.  These decisions are usually made by using abstract data types tested with different implementations.
&lt;/p&gt;

&lt;p&gt;
Choosing a representation is affected by the characteristics of the data and how it is used.  When little definitive can be said about these factors in advance or there is a wide range of possibilities in each, accurate analysis is important.  Furthermore, it may be the case that the structure of the data should change in accordance with its usage history and current environment.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic ADTs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
The difference between profiling functions and profiling data is that functions are an active agent in a computation.  When a function is executed, the mechanism behind the execution provides a hook with which we can monitor a function.  Data doesn't have such a counterpart &amp;mdash; it's inert.  In order to profile data, you have to associate something active with it.
&lt;/p&gt;

&lt;p&gt;
&lt;a href="http://wozniak.ca/projects/dynamic-adt/"&gt;Dynamic ADTs&lt;/a&gt; (DADTs) are something &lt;a href="http://www.international-lisp-conference.org/2007/speakers#wozniak_geoff"&gt;I came up with&lt;/a&gt; to try doing something with the idea of "active" data.  DADTs are wrappers for data objects that collect profiling information and allow you to attach functionality to the objects.  You associate functions (or methods) with the DADTs to provide points in a program where the information is collected and the functionality may be triggered.
&lt;/p&gt;

&lt;p&gt;
Put succinctly, DADTs allow you to collect information about how an object is used and act on it.
&lt;/p&gt;

&lt;p&gt;
Using DADTs, you can track the life of a data object.  For example, you can count the number of times certain operations are performed on the object or monitor the subcomponents of the data (say, noting the kinds of elements added to it if the object is a container).  With actions attached to the DADT object, you can adjust the object to better suit its use, perhaps by changing its representation.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A (contrived) example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
While I try to avoid contrived examples, I also don't want this post to get too long or involved &amp;mdash; something that would assuredly happen with a comprehensive demonstration.  Please note that the following is demonstrative, but little else.
&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;base-number&lt;/span&gt; &lt;span class="paren"&gt;()&lt;/span&gt;
  &lt;span class="paren"&gt;((&lt;/span&gt;number-value &lt;span class="builtin"&gt;:initarg&lt;/span&gt; &lt;span class="builtin"&gt;:number-value&lt;/span&gt;
                 &lt;span class="builtin"&gt;:initform&lt;/span&gt; 0
                 &lt;span class="builtin"&gt;:accessor&lt;/span&gt; number-value&lt;span class="paren"&gt;)))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;silly-number&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;base-number&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;())&lt;/span&gt;
&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defclass&lt;/span&gt; &lt;span class="type"&gt;dumb-number&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;base-number&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;())&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defgeneric&lt;/span&gt; &lt;span class="function-name"&gt;set-number-value&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;num val&lt;span class="paren"&gt;))&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;set-number-value&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;num silly-number&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;val integer&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;&amp;lt; &lt;span class="paren"&gt;(&lt;/span&gt;number-value num&lt;span class="paren"&gt;)&lt;/span&gt; val&lt;span class="paren"&gt;)&lt;/span&gt;
      &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;loop&lt;/span&gt; until &lt;span class="paren"&gt;(&lt;/span&gt;= &lt;span class="paren"&gt;(&lt;/span&gt;number-value num&lt;span class="paren"&gt;)&lt;/span&gt; val&lt;span class="paren"&gt;)&lt;/span&gt; do &lt;span class="paren"&gt;(&lt;/span&gt;incf &lt;span class="paren"&gt;(&lt;/span&gt;number-value num&lt;span class="paren"&gt;)))&lt;/span&gt;
      &lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;loop&lt;/span&gt; until &lt;span class="paren"&gt;(&lt;/span&gt;= &lt;span class="paren"&gt;(&lt;/span&gt;number-value num&lt;span class="paren"&gt;)&lt;/span&gt; val&lt;span class="paren"&gt;)&lt;/span&gt; do &lt;span class="paren"&gt;(&lt;/span&gt;decf &lt;span class="paren"&gt;(&lt;/span&gt;number-value num&lt;span class="paren"&gt;))))&lt;/span&gt;
  num&lt;span class="paren"&gt;)&lt;/span&gt;

&lt;span class="paren"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defmethod&lt;/span&gt; &lt;span class="function-name"&gt;set-number-value&lt;/span&gt; &lt;span class="paren"&gt;((&lt;/span&gt;num dumb-number&lt;span class="paren"&gt;)&lt;/span&gt; &lt;span class="paren"&gt;(&lt;/span&gt;val integer&lt;span class="paren"&gt;))&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;sleep 1&lt;span class="paren"&gt;)&lt;/span&gt;
  &lt;span class="paren"&gt;(&lt;/span&gt;setf &lt;span class="paren"&gt;(&lt;/span&gt;number-value num&lt;span class="paren"&gt;)&lt;/span&gt; val&lt;span class="paren"&gt;)&lt;/span&gt;
  num&lt;span class="paren"&gt;)&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;
There are two immediately obvious conclusions to be drawn from this code: that the example is truly contrived, and that there are certain situations where a silly number is going to be better than a dumb number and vice versa.
&lt;/p&gt;

&lt;p&gt;
When the difference between the current value of a number and what it is to be set to is large, a dumb number is going to be a better representation; otherwise, a silly number is better.  The difference between the current value and val parameter in set-number-value is called the &lt;em&gt;delta value&lt;/em&gt;.
&lt;/p&gt;

&lt;p&gt;
This shows the two major factors we have to consider before choosing which one to use in our program: we need to have a good idea as to the nature of the data we'll be working with and we need to know which one is going to be faster (it will vary with the architecture).   If the data is likely to exhibit large delta values between the setting of numbers, then we'll pick a dumb number (even with its absurd delay).  In any other case, a silly number is almost surely faster.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A DADT for numbers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
Using DADTs, we can take advantage of the behavioural differences between two classes that provide the same functionality.  In our example, this means we are going to switch between using a silly or dumb number when one is better than another.  To do that, we define a DADT class for numbers.  A DADT class does not inherit structure.  Instead, it is a wrapper object that holds an instance of the type it seeks to mimic.  It is also associated with other objects that collect information and act on it.
&lt;/p&gt;

&lt;p style="text-decoration: underline;"&gt;
&lt;em&gt;What kind of number and when?&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
The first step is figuring out how we detect when we want to use a silly number over a dumb number and vice versa.  As reasoned above, it can be found by computing the delta value.  Once we know the point at which it is faster to wait one second then set the value instead of counting up (or down) to the number, the logic behind choosing is easy.  This value will be known as the &lt;em&gt;delta threshold&lt;/em&gt;.
&lt;/p&gt;

&lt;p&gt;
Computing the delta value means comparing the current value of a number to what it will be set to, which can be done by observing the parameters passed to set-number-value.  Operations specifically written to work on DADTs give us access to this information through the use of triggers.  Triggers are functions attached to DADT objects that are run within the context of a DADT operation.  Using a trigger, we can compute the delta value and choose the number behaviour that is best suited to accomplish the operation.
&lt;/p&gt;

&lt;p&gt;
The trigger we're going to use for our number DADT is going to work in the following manner.
&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;If the current operation is set-number-value, compute the delta value.  Otherwise, do nothing.&lt;/li&gt;
  &lt;li&gt;If the delta value is less than the delta threshold and the number is currently behaving as a dumb number, change the number to act as a silly number.&lt;/li&gt;
  &lt;li&gt;If the delta value is greater than the delta threshold and the number is behaving as a silly number, then change the number to act as a dumb number.&lt;/li&gt;
  &lt;li&gt;In any other case, do nothing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p style="text-decoration: underline;"&gt;
&lt;em&gt;Computing the delta threshold&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
Now we need to figure out the delta threshold.  It is certainly possible to do this manually, but doing it programmatically would be cleaner.  We'll use the DADT framework.
&lt;/p&gt;

&lt;p&gt;
The complement to triggers are resources.  Resources are simply data stores.  A resource is defined by providing a class and adding methods on predefined generic functions.  The methods are used to take measurements that can be stored in instances of the given resource class.
&lt;/p&gt;

&lt;p&gt;
To compute the delta threshold, we can define a resource that measures the approximate run time of the set-number-value operation.  The timing information is used to get a reasonably accurate value for the delta threshold.  Conveniently, this isolated timing experiment can be done within our trigger and only if the delta threshold is unknown.  As long as we arrange for triggers to be run before the operation (which is the default) this will work.
&lt;/p&gt;

&lt;p style="text-decoration: underline;"&gt;
&lt;em&gt;Dealing with performance issues&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
The last element of the number DADT is that of performance.  DADTs can incur quite a bit of overhead and we want to mitigate it somewhat.  It's unlikely we'll be able to determine ahead of time exactly what number behaviour is best for all situations, so instead we'll look at defining a reasonable heuristic.
&lt;/p&gt;

&lt;p&gt;
If we were to track the history of a number object, we may find that one kind of number is better for the object that another.  For example, if the average delta value for a number is less than the delta threshold, we could just ignore any attempts to change the behaviour and stick with the behaviour of silly numbers.
&lt;/p&gt;

&lt;p&gt;
DADTs can achieve this using directives.  Directives are a rudimentary form of memory for DADT objects.  There are standard directives that can be used to indicate whether the extra work of collecting and using information on a DADT object is done.  Note that this is at the individual object level (although it can be applied globally).
&lt;/p&gt;

&lt;p&gt;
In order to "optimize" a number DADT object, we'll assume that if the object has had its value changed &lt;em&gt;N&lt;/em&gt; times and hasn't changed its behaviour, then it will be configured to keep that behaviour, that is, stabilize it.  This will be done through another trigger.
&lt;/p&gt;

&lt;p style="text-decoration: underline;"&gt;
&lt;em&gt;Summary&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;
In summary, to define the number DADT we will define a DADT class for numbers whose instances will behave as either silly or dumb numbers.  The operations for these numbers will be number-value and set-number-value.  A trigger will change the behaviour based on the delta value for the set-number-value operation; the delta value is also computed by the trigger when necessary.  A resource will be attached to each number DADT object that keeps track of the number of times the value of the number has been changed.  Another trigger will use this information to stabilize it when the conditions for doing so have been met.
&lt;/p&gt;

&lt;strong&gt;The DADT in action&lt;/strong&gt;

&lt;p&gt;
I will preclude going through the code to define the DADT because it is too lengthy for this post (which is long enough as is).  The &lt;a href="http://wozniak.ca/projects/dynamic-adt/example/number-example.html"&gt;code is available&lt;/a&gt;, for those who want to see.  (The following was done one a 2.16 GHz Intel Core Duo using SBCL 1.0.5.25.)
&lt;/p&gt;

&lt;p&gt;
For starters, let's take a look at what a number DADT object looks like. (Information irrelevant to the present discussion has been omitted.)
&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(describe (make-dnumber))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt;#&amp;lt;DNUMBER rep: SILLY-NUMBER {11C9EDD9}&amp;gt;
is an instance of class #&amp;lt;DYNAMIC-ADT-CLASS DNUMBER&amp;gt;.
The following slots have :INSTANCE allocation:
 REP                 #&amp;lt;SILLY-NUMBER value: 0  {11C9EE01}&amp;gt;
 LOCAL-TRIGGERS      (CONVERSION-TRIGGER STABILITY-TRIGGER)
 RESOURCES           (#&amp;lt;CHANGE-COUNTER {11C9EE21}&amp;gt;)&lt;/span&gt;
...&lt;/pre&gt;

&lt;p&gt;
Note that a dnumber has the behaviour of a silly number by default.  We can see that it has two triggers (the functions conversion-trigger and stability-trigger) and an instance of change-counter as its only resource object.
&lt;/p&gt;

&lt;p&gt;
At this point, the delta threshold has not been computed.  When setting a dnumber to a value, it will be computed if it hasn't already.
&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;*delta-threshold*&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(set-number-value (make-dnumber) 1)&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;#&amp;lt;DNUMBER rep: SILLY-NUMBER {1277C3C1}&amp;gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;*delta-threshold*&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;9872727&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(time (set-number-value (make-instance 'silly-number)
                                  *delta-threshold*))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt;Evaluation took:
  1.011 seconds of real time
  1.007832 seconds of user run time
  6.63e-4 seconds of system run time
  0 calls to %EVAL
  0 page faults and
  0 bytes consed.
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;#&amp;lt;SILLY-NUMBER value: 9872727  {12848CC9}&amp;gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
We can see that the delta threshold is reasonably accurate.
&lt;/p&gt;

&lt;p&gt;
Now we want to compare dnumbers to silly and dumb numbers.  We'll do this by creating an instance of a number and assigning it a series of values.  These values should cause the delta value to cross the delta threshold occasionally.  The second result below tells us what behaviour would be best when setting a number based on its previous value (starting at 0).
&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(defparameter *test-numbers*
            (loop repeat 50
                  collect (random (* 10 *delta-threshold*))))&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;*TEST-NUMBERS*&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(let ((curr 0))
            (mapcar
             #'(lambda (n)
                 (prog1
                     (if (&amp;lt;= (abs (- curr n)) *delta-threshold*)
                         'silly
                         'dumb)
                   (setf curr n)))
             *test-numbers*))&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;(DUMB DUMB DUMB DUMB DUMB DUMB DUMB DUMB SILLY SILLY
 SILLY DUMB DUMB DUMB DUMB SILLY DUMB DUMB DUMB SILLY
 DUMB DUMB SILLY SILLY DUMB DUMB SILLY DUMB DUMB DUMB
 DUMB DUMB DUMB DUMB SILLY SILLY DUMB DUMB SILLY SILLY
 DUMB SILLY SILLY DUMB DUMB DUMB DUMB SILLY DUMB DUMB)&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
When we set a dnumber to the test numbers, we should see &lt;em&gt;19&lt;/em&gt; conversions: one for each transition in the list (which accounts for 18) and one more because &amp;mdash; can you guess? &amp;mdash; a dnumber starts out as a silly number.
&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(defun run-test (num)
            (time
             (loop for n in *test-numbers*
                   do (set-number-value num n))))&lt;/span&gt;
&lt;span class="slime-repl-inputed-output"&gt;RUN-TEST&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(progn (run-test (make-instance 'dumb-number))
                 (run-test (make-instance 'silly-number))
                 (run-test (make-dnumber)))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt;Evaluation took:
  50.006 seconds of real time
  ...
Evaluation took:
  153.342 seconds of real time
  ...
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Converting #&amp;lt;DNUMBER rep: DUMB-NUMBER {122CC261}&amp;gt; to SILLY-NUMBER
Converting #&amp;lt;DNUMBER rep: SILLY-NUMBER {122CC261}&amp;gt; to DUMB-NUMBER
Evaluation took:
  41.465 seconds of real time
  ...
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
The timing for dumb numbers isn't surprising, but silly numbers demonstrate how horrible they are for large delta values.  The dnumbers manage to shave about 8.5 seconds off a dumb numbers' time and the expected conversions took place.
&lt;/p&gt;

&lt;p&gt;
And how about the stability notion?  We'll set the &lt;em&gt;N&lt;/em&gt; value mentioned earlier to 100 and see what the results are versus a "plain" silly number, a dnumber with the heuristic and a dnumber without the heuristic.
&lt;/p&gt;

&lt;pre class="repl"&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(let ((num (make-instance 'silly-number)))
            (time
             (loop repeat 10000
                   do (set-number-value num (random 100)))))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt;Evaluation took:
  0.039 seconds of real time
  ...
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(let ((num (make-dnumber)))
            (time
             (loop repeat 10000
                   do (set-number-value num (random 100)))))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt;Stabilizing #&amp;lt;DNUMBER rep: SILLY-NUMBER {11CAF209}&amp;gt;
Evaluation took:
  0.041 seconds of real time
  ...
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;span class="slime-repl-result"&gt;
&lt;/span&gt;&lt;span class="slime-repl-prompt"&gt;DNUMBERS&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(let ((num (make-dnumber)))
            (delete-local-trigger num 'stability-trigger)
            (time
             (loop repeat 10000
                   do (set-number-value num (random 100)))))&lt;/span&gt;
&lt;span class="slime-repl-output"&gt;Evaluation took:
  0.104 seconds of real time
  ...
&lt;/span&gt;&lt;span class="slime-repl-inputed-output"&gt;NIL&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;
The heuristic may not be perfect, but it does make a big difference. 
&lt;/p&gt;

&lt;p&gt;
Of course, this example is grossly oversimplified.  For one thing, dumb numbers and silly numbers have the same structure, so conversion between the two is very cheap.  Notice how the address of the internal representation of the dnumber didn't change?  That's because change-class is used to actually convert between dumb and stupid numbers.  In most cases, this isn't going to be the case.  Normally, we would have to take into account the cost of converting between different representations in order to achieve the behavioural change for the DADT object.  (The complexity of doing so is why I chose such a contrived example.)
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's cute, but what's the point?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;
This is all well and good, but the common objection to such things is that it has little to no practical value.  To some extent, sure.  In a production system built for speed, this kind of construct is of dubious use.  But if you look at the entire process of building such a system, they could come in handy.
&lt;/p&gt;

&lt;p&gt;
The act of programming is a process that involves the specialization of concepts.  Developers start out with a description of concepts which get realized in some particular fashion.  Along the way, they may find that one implementation may not capture a concept adequately; multiple implementations of the same concept may be needed.  Furthermore, a concept may not map neatly onto one particular realization so it is spread across different interfaces.  What DADTs try to do is capture the essence of a concept without including the code to manage interactions between different implementation elements directly.
&lt;/p&gt;

&lt;p&gt;
In short, DADTs provide a way to express the concept before is becomes specialized.  During development, this could be useful in employing concepts before deciding on specializations.  DADTs can even be used to determine specializations themselves &amp;mdash; the stability trigger given above is a simple example.
&lt;/p&gt;

&lt;p&gt;
Observation and adaptation are integral to the process of feedback.  I am fascinated by what feedback can do for software and software development; arguably, it's necessary.  Dynamic ADTs are my humble effort to see where it can lead.
&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5628552786429674762?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5628552786429674762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5628552786429674762' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5628552786429674762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5628552786429674762'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/05/using-data-profiling-in-running-system.html' title='Using data profiling in a running system'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-5235491302016354456</id><published>2007-05-22T21:45:00.001-05:00</published><updated>2007-05-22T21:45:35.524-05:00</updated><title type='text'>Human and computers probably shouldn't trust each other</title><content type='html'>&lt;p&gt;
Digging through the archives of computer science history can reveal some really interesting stuff.  Take the paper &lt;a href="http://fresh.homeunix.net/~luke/misc/p271-de_millo.pdf"&gt;Social Processes and Proofs of Theorems and Programs&lt;/a&gt; (pdf), recently discussed on &lt;a href="http://lambda-the-ultimate.org/node/2254"&gt;Lambda the Ultimate&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
The gist of the paper is that program verification is a red herring and resources dedicated to verification are probably misspent.  After arguing that proofs only get used when they are believed (and that a proof by itself is not enough to express truth), the meat of the argument is reached.
&lt;/p&gt;

&lt;blockquote&gt;
There is a fundamental logical objection to verification, an objection on its own ground of formalistic rigor.  Since the requirement for a program is informal and the program is formal, there must be a transition, and the transition itself must necessarily be informal. We have been distressed to learn that this proposition, which seems self-evident to us, is controversial. So we should emphasize that as antiformalists, we would not object to verification on these grounds; we only wonder how this inherently informal step fits into the formalist view. Have the adherents of verification lost sight of the informal origins of the formal objects they deal with? Is it their assertion that their formalizations are somehow incontrovertible? We must confess our confusion and dismay.
&lt;/blockquote&gt;

&lt;p&gt;
I have a hard time disagreeing with this observation.  I'd love for verification to be practical, but I just don't buy it due to the human factor.
&lt;/p&gt;

&lt;p&gt;
There is also a good discussion of continuity.  Formalists argue that programs are nothing more than algorithms and model programs.  The authors don't buy it.
&lt;/p&gt;

&lt;blockquote&gt;
It is with (2) [programs being nothing more than algorithms and model programs] that we have our fundamental disagreement. We argue that there is no continuity between the world of FIND or GCD and the world of production software, billing systems that write real bills, scheduling systems that schedule real events, ticketing systems that issue real tickets. And we argue that the world of production software is itself discontinuous.
&lt;/blockquote&gt;

&lt;p&gt;
One of the reasons I'm not enamoured by verification is that even if my program were verified, I still wouldn't trust it.  I'd feel compelled to test it and check in on it at regular intervals to make sure it was working.  After all, I had a hand in making it (what does that say?) and a computer informed me that I was doing fine.  Would this verification save me any time during my regularly scheduled scrutiny?  I have my doubts.
&lt;/p&gt;

&lt;p&gt;
The paper is rife with food for thought and refreshing perspectives, even though it was published 28 years ago.  I highly recommend it.
&lt;/p&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-5235491302016354456?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/5235491302016354456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=5235491302016354456' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5235491302016354456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/5235491302016354456'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/05/human-and-computers-probably-shouldn.html' title='Human and computers probably shouldn&amp;#39;t trust each other'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-1985930432559933226</id><published>2007-05-13T19:23:00.001-05:00</published><updated>2007-05-13T20:42:36.387-05:00</updated><title type='text'>The static/dynamic albatross</title><content type='html'>&lt;p&gt;Nothing strikes me as more pointless than the static/dynamic typing argument in the programming languages community.  So much time has been wasted on this topic that it almost brings a tear to my eye.&lt;/p&gt;

&lt;p&gt;That being said, there are sometimes diamonds in the perennial rough.  These quotes are representative of my views on the matter.&lt;/p&gt;

&lt;p&gt;Two from Kent M. Pitman in the c.l.lisp thread &lt;a href="http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/c175802fd02129cb/#"&gt;Does ANSI Common Lisp have pattern matching?&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;Language choices are not about Good vs Bad, they are about design trade-offs: you trade away the things you don't plan to do in favor of the things you do plan to do.  The hard part is getting that planning right so you aren't pinned into a corner down the line.  Since Lisp supports changing one's mind later, I tend to like that, but it comes at some occasional costs, and I think there's a minor but manageable cost on the issue of clarity of intent on certain intentional type issues.  It's not a cost that troubles me because it buys other things I care about.  Static typing feels like a stranglehold to me.  But it's worth acknowledging that there are people who will be troubled by these design trade-offs because they value things differently.&lt;/blockquote&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;blockquote&gt;The static/dynamic thing was not done as a way of saying, as a community, "we're too lazy to do things up front where they belong".  Rather, we as a community have said "we want to first choose what we want to say, and when the right time is to say it, and only after that is done will we see about making that maximally efficient".&lt;/blockquote&gt;

&lt;p&gt;Read the posts &lt;a href="http://groups.google.com/group/comp.lang.lisp/msg/0bb251d538514db7"&gt;here&lt;/a&gt; and &lt;a href="http://groups.google.com/group/comp.lang.lisp/msg/d2a47d874d722cc3"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, Anton van Straaten has &lt;a href="http://lambda-the-ultimate.org/node/683#comment-6038"&gt;a comment&lt;/a&gt; over at Lambda the Ultimate.&lt;/p&gt;

&lt;blockquote&gt;I'll state for the record that I don't like what I see as a false dichotomy in the static/dynamic debate — what I'm really looking forward to in the future is a more sophisticated attitude towards types from both camps. Dynamic advocates who see absolutely no benefit to static checks are missing the fact that the programs they write are full of static types which they actually reason about, even if they don't realize it; but static advocates who think that a universal type encapsulates everything there is to know about dynamic languages are, funnily enough, &lt;em&gt;missing the exact same point&lt;/em&gt;. (emphasis his)&lt;/blockquote&gt;

&lt;p&gt;I'm sure there will be plenty more rough of this nature in the future.  I at least hope that diamonds keep coming with it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-1985930432559933226?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/1985930432559933226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=1985930432559933226' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1985930432559933226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/1985930432559933226'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/05/staticdynamic-albatross.html' title='The static/dynamic albatross'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-2710353605149029355</id><published>2007-05-09T01:15:00.000-05:00</published><updated>2007-05-13T20:36:56.976-05:00</updated><title type='text'>Wrapping code using compiler macros</title><content type='html'>&lt;p&gt;Macros are probably my favourite thing about Lisp.  They were the reason I started using Common Lisp and I do a lot of work with them.  I guess I like manipulating programs, but I don't want to write new compilers.
&lt;/p&gt;

&lt;p&gt;Recently, I've been looking for ways to take an existing function definition and replace each occurrence of it in some source code that wraps the call to set up a specific environment.  Furthermore, I wanted to avoid the need to actually change the source code.  I thought it would be nice to share the way I went about doing this.  (Hey, that's what blogs are for, right?)  Plus, it gives a nice demonstration of what compiler macros are and how they work.
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;For demonstration purposes, here's a simple function definition that we'll use throughout.
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;defun&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;example&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;x y&lt;span style="color: #696969;"&gt;)&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;* x x y y&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The goal is to wrap each occurrence of &lt;span style="font-family: monospace;"&gt;example&lt;/span&gt; in the source code with code that will keep a history of the second argument.  That is, throughout the entire program, we want to remember all the values that were passed as the second argument.
&lt;/p&gt;

&lt;p&gt;There are certainly other ways to do this (using around methods, for example), but for the sake of illustration, we'll ignore them.&lt;br /&gt;&lt;br /&gt;Ultimately, we want to turn this
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #696969;"&gt;(&lt;/span&gt;example x y&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;into something like this
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #b22222;"&gt;;; &lt;/span&gt;&lt;span style="color: #b22222;"&gt;You only want to evaluate the second argument once.
&lt;/span&gt;&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;let&lt;/span&gt; &lt;span style="color: #696969;"&gt;((&lt;/span&gt;tmp y&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;add-value-to-history tmp&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;example x tmp&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;A solution using compiler macros&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;Recall that compiler macros are macros that are applied only during compilation.  Although it is optional for an implementation to apply them, I'm going to assume that the implementation does.
&lt;/p&gt;

&lt;p&gt;One very important property of compiler macros is that if the same form to be expanded is returned, no expansion is done.  This is in contrast to regular macros in that they must always provide an expansion that is not the same as the form to expand lest you end up in an infinite loop.
&lt;/p&gt;

&lt;p&gt;How can we take advantage of this?  The idiom for this is to insert a macro into the enclosing lexical environment and decline to expand the form if the macro is present in the environment.
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;defparameter&lt;/span&gt; &lt;span style="color: #b8860b;"&gt;*value-history*&lt;/span&gt; nil&lt;span style="color: #696969;"&gt;)&lt;/span&gt;

&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;defun&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;add-value-to-history&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;val&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;push val *value-history*&lt;span style="color: #696969;"&gt;))&lt;/span&gt;

&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;defun&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;expands-in-environment-p&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;form env&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;second &lt;span style="color: #696969;"&gt;(&lt;/span&gt;multiple-value-list &lt;span style="color: #696969;"&gt;(&lt;/span&gt;macroexpand-1 form env&lt;span style="color: #696969;"&gt;))))&lt;/span&gt;

&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;define-compiler-macro&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;example&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #228b22;"&gt;&amp;amp;whole&lt;/span&gt; form x y &lt;span style="color: #228b22;"&gt;&amp;amp;environment&lt;/span&gt; env&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;if&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;expands-in-environment-p '&lt;span style="color: #696969;"&gt;(&lt;/span&gt;%foo%&lt;span style="color: #696969;"&gt;)&lt;/span&gt; env&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
      form
      &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;let&lt;/span&gt; &lt;span style="color: #696969;"&gt;((&lt;/span&gt;tmp &lt;span style="color: #696969;"&gt;(&lt;/span&gt;gensym&lt;span style="color: #696969;"&gt;)))&lt;/span&gt;
        `&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;macrolet&lt;/span&gt; &lt;span style="color: #696969;"&gt;((&lt;/span&gt;%foo% &lt;span style="color: #696969;"&gt;()&lt;/span&gt; t&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
           &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;let&lt;/span&gt; &lt;span style="color: #696969;"&gt;((&lt;/span&gt;,tmp ,y&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
             &lt;span style="color: #696969;"&gt;(&lt;/span&gt;add-value-to-history ,tmp&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
             &lt;span style="color: #696969;"&gt;(&lt;/span&gt;example ,x ,tmp&lt;span style="color: #696969;"&gt;))))))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;We can try it out to see if it works as advertised.
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;(funcall (compiler-macro-function 'example)
                  '(example 1 2)
                  nil)
&lt;span style="color: #ff0000;"&gt;(MACROLET ((%FOO% NIL T))
  (LET ((#:G177 2)) (ADD-VALUE-TO-HISTORY #:G177) (EXAMPLE 1 #:G177)))&lt;/span&gt;
&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;(macrolet ((%foo% () t))
           (macrolet ((check (&amp;amp;environment env)
                        `(quote ,(funcall
                                   (compiler-macro-function 'example)
                                   '(example 1 2)
                                   env))))
             (check)))
&lt;span style="color: #ff0000;"&gt;(EXAMPLE 1 2)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note that the second example sets up the environment so that the compiler macro doesn't expand the form.  We use the &lt;span style="font-family: monospace;"&gt;check&lt;/span&gt; macro so that we can get an environment object to pass to the compiler macro function.
&lt;/p&gt;

&lt;p&gt;We're not quite done yet.  The next step is to recompile the code that uses &lt;span style="font-family: monospace;"&gt;example&lt;/span&gt;.  Remember, compiler macros are only expanded by the compiler, which means simply running code that uses &lt;span style="font-family: monospace;"&gt;example&lt;/span&gt; isn't enough.
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;defun&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;foo&lt;/span&gt; &lt;span style="color: #696969;"&gt;()&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;loop&lt;/span&gt;
     repeat 10
     for i = &lt;span style="color: #696969;"&gt;(&lt;/span&gt;random 100&lt;span style="color: #696969;"&gt;)&lt;/span&gt; and j = &lt;span style="color: #696969;"&gt;(&lt;/span&gt;random 100&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
     collect &lt;span style="color: #696969;"&gt;(&lt;/span&gt;example i j&lt;span style="color: #696969;"&gt;)))&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Now we'll test to see if everything is working.
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;(foo)
&lt;span style="color: #ff0000;"&gt;(10890000 5793649 14561856 7617600 327184 33918976 980100 3418801
 467856 2509056)&lt;/span&gt;
&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;*value-history*
&lt;span style="color: #ff0000;"&gt;NIL&lt;/span&gt;
&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;(compile 'foo)
&lt;span style="color: #ff0000;"&gt;FOO&lt;/span&gt;
&lt;span style="color: #ff0000;"&gt;NIL&lt;/span&gt;
&lt;span style="color: #ff0000;"&gt;NIL&lt;/span&gt;
&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;(foo)
&lt;span style="color: #ff0000;"&gt;(944784 2446096 659344 49702500 0 792100 13924 19377604 15023376 290521)&lt;/span&gt;
&lt;span style="color: #a020f0;"&gt;CL-USER&amp;gt; &lt;/span&gt;*value-history*
&lt;span style="color: #ff0000;"&gt;(77 51 62 59 10 0 94 28 17 12)&lt;/span&gt;
&lt;/pre&gt;

&lt;p&gt;Excellent!
&lt;/p&gt;

&lt;p&gt;It turns out there is an even easier way to prevent recursive expansion of compiler macros.  According to the &lt;a href="http://franz.com/support/documentation/8.0/ansicl/subsubsu/whencomp.htm"&gt;HyperSpec&lt;/a&gt;, if a function name is declared as &lt;span style="font-family: monospace;"&gt;notinline&lt;/span&gt; and the call appears within the scope of this declaration, then the compiler macro is not applied.  Our compiler macro gets a little simpler.
&lt;/p&gt;
&lt;pre&gt;&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;define-compiler-macro&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;example&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #228b22;"&gt;&amp;amp;whole&lt;/span&gt; form x y&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;declare&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;ignore form&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
  &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;let&lt;/span&gt; &lt;span style="color: #696969;"&gt;((&lt;/span&gt;tmp &lt;span style="color: #696969;"&gt;(&lt;/span&gt;gensym&lt;span style="color: #696969;"&gt;)))&lt;/span&gt;
    `&lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;let&lt;/span&gt; &lt;span style="color: #696969;"&gt;((&lt;/span&gt;,tmp ,y&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
       &lt;span style="color: #696969;"&gt;(&lt;/span&gt;&lt;span style="color: #a020f0;"&gt;declare&lt;/span&gt; &lt;span style="color: #696969;"&gt;(&lt;/span&gt;notinline example&lt;span style="color: #696969;"&gt;))&lt;/span&gt;
       &lt;span style="color: #696969;"&gt;(&lt;/span&gt;add-value-to-history ,tmp&lt;span style="color: #696969;"&gt;)&lt;/span&gt;
       &lt;span style="color: #696969;"&gt;(&lt;/span&gt;example ,x ,tmp&lt;span style="color: #696969;"&gt;))))&lt;/span&gt;
&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Subtleties&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;Above I used a call to &lt;span style="font-family: monospace;"&gt;compile&lt;/span&gt; to compile &lt;span style="font-family: monospace;"&gt;foo&lt;/span&gt;, but that only worked because &lt;span style="font-family: monospace;"&gt;foo&lt;/span&gt; wasn't a compiled function yet.  If you redefine the compiler macro, you have to recompile everything.  Unfortunately, there isn't a way to recompile an already compiled function without re-evaluating its definition.  Thus, my presentation of compilation is simplistic.  Since I do most of my Lisp coding in an IDE of some form (Slime, Allegro, LispWorks), recompilation isn't usually a big deal.  In the future, I may look into a way to use &lt;span style="font-family: monospace;"&gt;asdf&lt;/span&gt; to make it a little easier.
&lt;/p&gt;

&lt;!-- technorati tags start --&gt;&lt;p style="text-align:right;font-size:10px;"&gt;Technorati Tags: &lt;a href="http://www.technorati.com/tag/lisp" rel="tag"&gt;lisp&lt;/a&gt;, &lt;a href="http://www.technorati.com/tag/macros" rel="tag"&gt;macros&lt;/a&gt;&lt;/p&gt;&lt;!-- technorati tags end --&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-2710353605149029355?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exploring-lisp.blogspot.com/feeds/2710353605149029355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2209407496177464044&amp;postID=2710353605149029355' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/2710353605149029355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/2710353605149029355'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/05/wrapping-code-using-compiler-macros_08.html' title='Wrapping code using compiler macros'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2209407496177464044.post-144781580231770821</id><published>2007-04-18T00:25:00.000-05:00</published><updated>2007-04-18T00:27:52.754-05:00</updated><title type='text'>A different place to post my Lisp stuff</title><content type='html'>I've decided to move all my Lisp related posts to here instead of &lt;a href="http://wozniak.ca/"&gt;my usual blog&lt;/a&gt;.  This is mainly for the benefit of friends and family who don't care about Lisp stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2209407496177464044-144781580231770821?l=exploring-lisp.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/144781580231770821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2209407496177464044/posts/default/144781580231770821'/><link rel='alternate' type='text/html' href='http://exploring-lisp.blogspot.com/2007/04/different-place-to-post-my-lisp-stuff.html' title='A different place to post my Lisp stuff'/><author><name>Geoff Wozniak</name><uri>http://www.blogger.com/profile/07143770726013580744</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://i211.photobucket.com/albums/bb52/GeoffWozniak/smallportrait.jpg'/></author></entry></feed>
