<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CkUGQ3c-eyp7ImA9WhBQEEg.&quot;"><id>tag:blogger.com,1999:blog-32049549</id><updated>2013-03-11T18:03:42.953-07:00</updated><category term="soda" /><category term="xml" /><category term="poo" /><category term="emacs" /><category term="rogue" /><category term="Einstein" /><category term="java" /><category term="erlang" /><category term="systems" /><category term="cl" /><category term="programming" /><category term="smalltalk" /><category term="Newton" /><category term="alaska" /><category term="oop" /><category term="lisp" /><category term="concurrency" /><category term="distributed computing" /><category term="star wars" /><category term="c" /><title>A Nickel's Worth</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://a-nickels-worth.blogspot.com/" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/blogspot/xuFvSR" /><feedburner:info uri="blogspot/xufvsr" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CUcDQXc6eCp7ImA9WxJTGUk.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-6435693063962274372</id><published>2009-04-28T11:05:00.000-07:00</published><updated>2009-04-28T11:24:30.910-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-28T11:24:30.910-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="systems" /><category scheme="http://www.blogger.com/atom/ns#" term="distributed computing" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Six Audacious Goals for Your System</title><content type="html">With apologies to the authors of the &lt;a href="http://www.graficaobscura.com/future/futnotes.html"&gt;futurist programming notes&lt;/a&gt;, here is a set of audacious goals for your system (by which I mean your collection of web sites, webservices, databases, and so on):&lt;div&gt;&lt;ol&gt;&lt;li&gt;Make your &lt;b&gt;entire &lt;/b&gt;system runnable (for debugging/development) on a single machine, via a single command,&lt;/li&gt;&lt;li&gt;...without manual configuration,&lt;/li&gt;&lt;li&gt;...without an Internet connection.&lt;/li&gt;&lt;li&gt;Demonstrate that your system works flawlessly even when you permanently and unexpectedly unplug the power cable from any one machine (even your most precious database).&lt;/li&gt;&lt;li&gt;Make one of your N-tier architectures M-tier, where M &amp;lt; N.&lt;/li&gt;&lt;li&gt;Optimize every &lt;b&gt;user-visible&lt;/b&gt; action so that it is perceived to complete in less than one second.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/qbIzEiElCMo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/6435693063962274372/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=6435693063962274372" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6435693063962274372?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6435693063962274372?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/qbIzEiElCMo/six-audacious-goals-for-your-system.html" title="Six Audacious Goals for Your System" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>0</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2009/04/six-audacious-goals-for-your-system.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEcBR30-eyp7ImA9WxdSFE4.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-6941429251761406941</id><published>2008-05-21T22:11:00.000-07:00</published><updated>2008-05-21T23:00:56.353-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-05-21T23:00:56.353-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Ulrich Drepper's Memory Paper and CL</title><content type="html">I recently came across Ulrich Drepper's excellent paper, &lt;a href="http://udrepper.livejournal.com/19557.html"&gt;What Every Programmer Should Know About Memory&lt;/a&gt;.  There is a lot of fascinating stuff in there about an important class of things you &lt;i&gt;sometimes&lt;/i&gt; need to do to achieve excellent performance.  While the paper concentrates on C, I was wondering if some of the same effects could be observed in a high-level language like &lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;CL&lt;/a&gt; (for the record, I think CL is &lt;i&gt;both&lt;/i&gt; high- and low-level, but whatever...)  I did a quick experiment which &lt;i&gt;suggests&lt;/i&gt; that at least one of Ulrich's optimizations works in CL.  [Note: the following is &lt;i&gt;not&lt;/i&gt; an attempt to produce mathematically correct results, as Ulrich did; I just wanted to see if the order in which memory was accessed seemed to matter.]:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;declaim&lt;/span&gt; (optimize (speed 3) (safety 0)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;matrix-multiply-fast?&lt;/span&gt; ()&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((m1 (make-array '(1000 1000) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'fixnum))&lt;br /&gt;        (m2 (make-array '(1000 1000) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'fixnum))&lt;br /&gt;        (m3 (make-array '(1000 1000) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'fixnum)))&lt;br /&gt;    (&lt;span class="keyword"&gt;loop&lt;/span&gt; repeat 100&lt;br /&gt;       do&lt;br /&gt;       (&lt;span class="keyword"&gt;loop&lt;/span&gt; for i upto 1000&lt;br /&gt;          do&lt;br /&gt;          (&lt;span class="keyword"&gt;loop&lt;/span&gt; for j upto 1000&lt;br /&gt;             do&lt;br /&gt;             (setf (aref m3 i j) (* (aref m1 i j) (aref m2 i j))))))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;matrix-multiply-slow?&lt;/span&gt; ()&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((m1 (make-array '(1000 1000) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'fixnum))&lt;br /&gt;        (m2 (make-array '(1000 1000) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'fixnum))&lt;br /&gt;        (m3 (make-array '(1000 1000) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'fixnum)))&lt;br /&gt;    (&lt;span class="keyword"&gt;loop&lt;/span&gt; repeat 100&lt;br /&gt;       do&lt;br /&gt;       (&lt;span class="keyword"&gt;loop&lt;/span&gt; for i upto 1000&lt;br /&gt;          do&lt;br /&gt;          (&lt;span class="keyword"&gt;loop&lt;/span&gt; for j upto 1000&lt;br /&gt;             do&lt;br /&gt;             (setf (aref m3 i j) (* (aref m1 i j) (aref m2 j i))))))))&lt;/pre&gt;Note the only difference between the two functions is the order in which the elements of the 2nd array are accessed.  After compiling the above file I did the following from the CL prompt:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(time (matrix-multiply-fast?))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-output"&gt;Evaluation took:&lt;br /&gt;  2.776 seconds of real time&lt;br /&gt;  2.508157 seconds of user run time&lt;br /&gt;  0.048003 seconds of system run time&lt;br /&gt;  [Run times include 0.028 seconds GC run time.]&lt;br /&gt;  0 calls to %EVAL&lt;br /&gt;  0 page faults and&lt;br /&gt;  12,000,160 bytes consed.&lt;/span&gt;&lt;span class="slime-repl-result"&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(time (matrix-multiply-slow?))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-output"&gt;Evaluation took:&lt;br /&gt;  3.926 seconds of real time&lt;br /&gt;  3.632227 seconds of user run time&lt;br /&gt;  0.016001 seconds of system run time&lt;br /&gt;  0 calls to %EVAL&lt;br /&gt;  0 page faults and&lt;br /&gt;  12,008,280 bytes consed.&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;Assuming the above is all correct, it sort-of duplicates Ulrich's result where accessing an array in a column-major order is slower (see figure 6.1 for an illustration).  Now, I have to admit that, for lack of time, I didn't read Ulrich's paper in great depth and kind of lazily jumped to section six.  I'd be curious to know if this sort of optimization is already well-known in the CL world (and/or for other "high-level" programming languages).  Some casual googling turned up an excellent &lt;a href="http://www.lrde.epita.fr/~didier/research/verna.06.imecs.pdf"&gt;paper&lt;/a&gt; about optimizing CL via type annotations, but I didn't see anyone directly addressing Ulrich's points.  I'd be curious if any readers of this blog know more.&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/wQCnb83IYtk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/6941429251761406941/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=6941429251761406941" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6941429251761406941?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6941429251761406941?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/wQCnb83IYtk/ulrich-dreppers-memory-paper-and-cl.html" title="Ulrich Drepper's Memory Paper and CL" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>1</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2008/05/ulrich-dreppers-memory-paper-and-cl.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UESXg_cSp7ImA9WxZUFUw.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-6339151957936058494</id><published>2008-03-23T23:58:00.000-07:00</published><updated>2008-04-06T12:46:48.649-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-04-06T12:46:48.649-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><title>Optimizing CL</title><content type="html">This blog is about mechanically optimizing &lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;CL&lt;/a&gt; code.  We will not discuss optimizing algorithms; instead we'll transform the code in rote ways to improve performance.  To illustrate these techniques, we'll re-implement Perl's core &lt;b&gt;Text::Soundex&lt;/b&gt; function in CL.  Let's start with the Perl code, straight from the 5.8.8 distribution.&lt;pre class="code"&gt;$&lt;span class="variable-name"&gt;soundex_nocode&lt;/span&gt; = undef;&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;sub&lt;/span&gt; &lt;span class="function-name"&gt;soundex&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt; &lt;span class="type"&gt;local&lt;/span&gt; (@&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;s&lt;/span&gt;&lt;/span&gt;, $&lt;span class="variable-name"&gt;f&lt;/span&gt;, $&lt;span class="variable-name"&gt;fc&lt;/span&gt;, $&lt;span class="variable-name"&gt;_&lt;/span&gt;) = @&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;_&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; push @&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;s&lt;/span&gt;&lt;/span&gt;, &lt;span class="string"&gt;''&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; @&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;s&lt;/span&gt;&lt;/span&gt;;        &lt;span class="comment"&gt;# handle no args as a single empty string&lt;br /&gt;&lt;/span&gt;&lt;br /&gt; &lt;span class="keyword"&gt;foreach&lt;/span&gt; (@&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;s&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt; {&lt;br /&gt;   $&lt;span class="variable-name"&gt;_&lt;/span&gt; = uc $&lt;span class="variable-name"&gt;_&lt;/span&gt;;&lt;br /&gt;   tr&lt;span class="string"&gt;/A-Z//&lt;/span&gt;cd;&lt;br /&gt;&lt;br /&gt;   &lt;span class="keyword"&gt;if&lt;/span&gt; ($&lt;span class="variable-name"&gt;_&lt;/span&gt; eq &lt;span class="string"&gt;''&lt;/span&gt;)&lt;br /&gt;   {&lt;br /&gt;     $&lt;span class="variable-name"&gt;_&lt;/span&gt; = $&lt;span class="variable-name"&gt;soundex_nocode&lt;/span&gt;;&lt;br /&gt;   }&lt;br /&gt;   &lt;span class="keyword"&gt;else&lt;/span&gt;&lt;br /&gt;   {&lt;br /&gt;     ($&lt;span class="variable-name"&gt;f&lt;/span&gt;) = &lt;span class="string"&gt;/^(.)/&lt;/span&gt;;&lt;br /&gt;     tr&lt;span class="string"&gt;/AEHIOUWYBFPVCGJKQSXZDTLMNR/00000000111122222222334556/&lt;/span&gt;;&lt;br /&gt;     ($&lt;span class="variable-name"&gt;fc&lt;/span&gt;) = &lt;span class="string"&gt;/^(.)/&lt;/span&gt;;&lt;br /&gt;     s&lt;span class="string"&gt;/^$fc+//&lt;/span&gt;;&lt;br /&gt;     tr&lt;span class="string"&gt;///&lt;/span&gt;cs;&lt;br /&gt;     tr&lt;span class="string"&gt;/0//&lt;/span&gt;d;&lt;br /&gt;     $&lt;span class="variable-name"&gt;_&lt;/span&gt; = $&lt;span class="variable-name"&gt;f&lt;/span&gt; . $&lt;span class="variable-name"&gt;_&lt;/span&gt; . &lt;span class="string"&gt;'000'&lt;/span&gt;;&lt;br /&gt;     s&lt;span class="string"&gt;/^(.{4}).*/$1/&lt;/span&gt;;&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; wantarray ? @&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;s&lt;/span&gt;&lt;/span&gt; : shift @&lt;span class="underline"&gt;&lt;span class="variable-name"&gt;s&lt;/span&gt;&lt;/span&gt;;&lt;br /&gt;}&lt;/pre&gt;We do not implement the auxiliary behavior of the Perl version, namely we do not optionally accept a list of strings, nor do we support overriding the &lt;b&gt;NIL&lt;/b&gt; return case.  Both would be easy to implement but would be tangential to this blog entry.  We port the rest of the functionality as faithfully as possible, though, so that the Perl can serve as a useful performance benchmark.&lt;br /&gt;&lt;br /&gt;Perl's &lt;b&gt;soundex&lt;/b&gt; uses regular expressions and string substitution operators heavily, some having analogues in CL and some not.  For example, CL lacks Perl's &lt;a href="http://perldoc.perl.org/functions/tr.html"&gt;tr///&lt;/a&gt; operator, so we implement a crude version:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defparameter&lt;/span&gt; &lt;span class="variable-name"&gt;*ascii-table*&lt;/span&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((table (make-array '(256) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'character)))&lt;br /&gt;                             (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;                                for i below 256&lt;br /&gt;                                do (setf (aref table i) (code-char i)))&lt;br /&gt;                             table))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;tr&lt;/span&gt; (string from-table to-table)&lt;br /&gt; &lt;span class="doc"&gt;"Crude version of Perl's tr/// operator."&lt;/span&gt;&lt;br /&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((table (copy-seq *ascii-table*)))&lt;br /&gt;   (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;      for from-char across from-table&lt;br /&gt;      and to-char across to-table&lt;br /&gt;      do (setf (aref table (char-code from-char)) to-char))&lt;br /&gt;   (map 'string&lt;br /&gt;        #'(&lt;span class="keyword"&gt;lambda&lt;/span&gt; (c) (aref table (char-code c)))&lt;br /&gt;        string)))&lt;br /&gt;&lt;/pre&gt;Our &lt;b&gt;TR&lt;/b&gt; supports only the limited case needed by &lt;b&gt;SOUNDEX&lt;/b&gt; (i.e., mapping one set of characters to another).  The Perl version can do more, such as removing letters that don't appear in the first set, and removing duplicates.  In fact, the Perl &lt;b&gt;soundex&lt;/b&gt; relies on that ability to remove duplicates, so we implement a function to do that as well.&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;uniq!&lt;/span&gt; (seq)&lt;br /&gt; (&lt;span class="keyword"&gt;cond&lt;/span&gt;&lt;br /&gt;   ((&amp;gt; (length seq) 1)&lt;br /&gt;    (&lt;span class="keyword"&gt;do*&lt;/span&gt; ((cur 0)&lt;br /&gt;          (cur-elt (elt seq cur) (elt seq cur))&lt;br /&gt;          (next 1 (1+ next)))&lt;br /&gt;         ((&amp;gt;= next (length seq)) (subseq seq 0 (1+ cur)))&lt;br /&gt;      (&lt;span class="keyword"&gt;let&lt;/span&gt; ((next-char (elt seq next)))&lt;br /&gt;        (&lt;span class="keyword"&gt;unless&lt;/span&gt; (eql cur-elt next-char)&lt;br /&gt;          (incf cur)&lt;br /&gt;          (setf (elt seq cur) next-char)))))&lt;br /&gt;   (t seq)))&lt;/pre&gt;&lt;b&gt;UNIQ!&lt;/b&gt; coalesces adjacent duplicate items into one item.  CL's built-in &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_remove-du_e-duplicates.html"&gt;DELETE-DUPLICATES&lt;/a&gt;&lt;/b&gt; doesn't work because it coalesces &lt;i&gt;all&lt;/i&gt; duplicates, not just adjacent ones.&lt;br /&gt;&lt;br /&gt;These two utility functions make porting the rest of the &lt;b&gt;soundex&lt;/b&gt; easy.  The following shows the CL equivalents of each important line of &lt;b&gt;soundex&lt;/b&gt;:&lt;table class="code"&gt;&lt;tr&gt;&lt;th&gt;Perl&lt;/th&gt;&lt;th&gt;What It Does&lt;/th&gt;&lt;th&gt;CL Equivalent&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;$&lt;span class="variable-name"&gt;_&lt;/span&gt; = uc $&lt;span class="variable-name"&gt;_&lt;/span&gt;;&lt;/td&gt;&lt;td&gt;Uppercase the string&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_string-up_g-capitalize.html"&gt;string-upcase&lt;/a&gt; ...)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tr&lt;span class="string"&gt;/A-Z//&lt;/span&gt;cd;&lt;/td&gt;&lt;td&gt;Remove any non-upper-alpha characters&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_removecm__elete-if-not.html"&gt;remove-if-not&lt;/a&gt; 'alpha-char-p string)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;($&lt;span class="variable-name"&gt;f&lt;/span&gt;) = &lt;span class="string"&gt;/^(.)/&lt;/span&gt;;&lt;/td&gt;&lt;td&gt;Gets the first character of the string&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_char_.htm"&gt;char&lt;/a&gt; s 0)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tr&lt;span class="string"&gt;/AE.../00.../&lt;/span&gt;;&lt;/td&gt;&lt;td&gt;Map letters to digits values&lt;/td&gt;&lt;td&gt;(tr s &lt;span class="string"&gt;"AE..."&lt;/span&gt; &lt;span class="string"&gt;"00..."&lt;/span&gt;)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;s&lt;span class="string"&gt;/^$fc+//&lt;/span&gt;;&lt;/td&gt;&lt;td&gt;Remove leading copies of the character in $fc&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_string-tr_g-right-trim.html"&gt;string-left-trim&lt;/a&gt; (vector fc) s2)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tr&lt;span class="string"&gt;///&lt;/span&gt;cs;&lt;/td&gt;&lt;td&gt;Remove adjacent duplicates&lt;/td&gt;&lt;td&gt;(uniq! ...)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;tr&lt;span class="string"&gt;/0//&lt;/span&gt;d;&lt;/td&gt;&lt;td&gt;Delete any '0' characters&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_removecm__elete-if-not.html"&gt;delete&lt;/a&gt; #\0 ...)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;$&lt;span class="variable-name"&gt;_&lt;/span&gt; = $&lt;span class="variable-name"&gt;f&lt;/span&gt; . $&lt;span class="variable-name"&gt;_&lt;/span&gt; . &lt;span class="string"&gt;'000'&lt;/span&gt;;&lt;/td&gt;&lt;td&gt;Concatenate, plus ensure length of at least 4&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_concatenate.html"&gt;concatenate&lt;/a&gt; 'string ...)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;s&lt;span class="string"&gt;/^(.{4}).*/$1/&lt;/span&gt;;&lt;/td&gt;&lt;td&gt;Strip off all but the first 4 characters&lt;/td&gt;&lt;td&gt;(&lt;a href="http://www.lisp.org/HyperSpec/Body/acc_subseq.html"&gt;subseq&lt;/a&gt; ... 0 4)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;Here is the actual code:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex&lt;/span&gt; (string)&lt;br /&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((s (string-upcase (remove-if-not 'alpha-char-p string))))&lt;br /&gt;   (&lt;span class="keyword"&gt;when&lt;/span&gt; (plusp (length s))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((f (char s 0)))&lt;br /&gt;       (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((s2 (tr s &lt;span class="string"&gt;"AEHIOUWYBFPVCGJKQSXZDTLMNR"&lt;/span&gt; &lt;span class="string"&gt;"00000000111122222222334556"&lt;/span&gt;))&lt;br /&gt;              (fc (char s2 0)))&lt;br /&gt;         (setf s2 (delete #\0&lt;br /&gt;                          (uniq! (string-left-trim (vector fc) s2))))&lt;br /&gt;         (subseq (concatenate 'string (vector f) s2 &lt;span class="string"&gt;"000"&lt;/span&gt;) 0 4))))))&lt;/pre&gt;&lt;br /&gt;Now let's see if it works.  Using &lt;a href="http://common-lisp.net/project/slime/"&gt;SLIME&lt;/a&gt;, type &lt;b&gt;C-c C-k&lt;/b&gt; to compile the file.  Then try &lt;b&gt;SOUNDEX&lt;/b&gt; at the CL prompt:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(soundex "supercalifrag")&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-result"&gt;&lt;span class="slime-repl-inputed-output"&gt;"S162"&lt;/span&gt;&lt;/span&gt;&lt;span class="slime-repl-result"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;If you try the Perl version, you'll find it returns the same thing (I tried other test cases, as well, but they are not relevant to this blog).&lt;br /&gt;&lt;br /&gt;At this point you might be feeling rather proud of yourself, after all you ported that Perl code pretty quickly, right?  And I bet it even performs better already; after all Perl is interpreted and CL is compiled!  Let's verify that assumption using the &lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_time.htm"&gt;&lt;b&gt;TIME&lt;/b&gt;&lt;/a&gt; macro built in to CL:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(time (dotimes (i 100000) (soundex "supercalifrag")))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-output"&gt;Evaluation took:&lt;br /&gt; 2.644 seconds of real time&lt;br /&gt; 2.640165 seconds of user run time&lt;br /&gt; 0.012001 seconds of system run time&lt;br /&gt; [Run times include 0.076 seconds GC run time.]&lt;br /&gt; 0 calls to %EVAL&lt;br /&gt; 0 page faults and&lt;br /&gt; 207,987,480 bytes consed.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;Now let's compare that to the Perl code's performance:&lt;pre class="code"&gt;&lt;span class="comint-highlight-prompt"&gt;$ &lt;/span&gt;&lt;span class="comint-highlight-input"&gt;time ./soundex-bench.pl 100000&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;real    0m1.069s&lt;br /&gt;user    0m1.064s&lt;br /&gt;sys     0m0.008s&lt;/div&gt;&lt;/pre&gt;D'oh!  The Perl code &lt;i&gt;kicked our ass&lt;/i&gt;!  It's more than twice as fast as our CL!  How could this have happened to us?  Maybe we forgot to turn on some optimizations?  We add: &lt;pre class="code"&gt;(&lt;span class="keyword"&gt;declaim&lt;/span&gt; (optimize (speed 3) (safety 0)))&lt;/pre&gt; to the top of our file and recompile.  Now let's see the results:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(time (dotimes (i 100000) (soundex "supercalifrag")))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-output"&gt;Evaluation took:&lt;br /&gt; 2.061 seconds of real time&lt;br /&gt; 2.040128 seconds of user run time&lt;br /&gt; 0.020001 seconds of system run time&lt;br /&gt; [Run times include 0.1 seconds GC run time.]&lt;br /&gt; 0 calls to %EVAL&lt;br /&gt; 0 page faults and&lt;br /&gt; 183,988,752 bytes consed.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;Ok, that's a &lt;i&gt;little&lt;/i&gt; better, but come &lt;i&gt;on&lt;/i&gt;, it is still approximately &lt;i&gt;twice&lt;/i&gt; as slow as the Perl!  And look at how much memory is allocated ("consed") in the course of doing only 100,000 calls: that's approximately 175 megabytes (admittedly not all at once, but still, that's just plain embarrassing!)&lt;br /&gt;&lt;br /&gt;Now before diving into optimization, let us review a good approach to optimizing CL.&lt;ol&gt;&lt;li&gt;Measure first.&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Avoid guessing!&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Fix your algorithm(s) first (not shown in this blog entry).&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Fix memory consumption next.&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Then go after CPU consumption, primarily by adding type information.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Remember, this blog is &lt;i&gt;not&lt;/i&gt; about algorithmic optimizations; we will pretend (for the sake of illustration only!) that you've already ruled out the need, in order to focus on mechanical optimization.&lt;br /&gt;&lt;br /&gt;Following step one from the above strategy, start by profiling.  Define a function, &lt;b&gt;MANY-SOUNDEX&lt;/b&gt; that performs our &lt;b&gt;TIME&lt;/b&gt; loop from above.  Also define a function &lt;b&gt;PROFILE-SOUNDEX&lt;/b&gt; that employs &lt;a href="http://sbcl.sourceforge.net/"&gt;SBCL&lt;/a&gt;'s &lt;a href="http://www.sbcl.org/manual/Deterministic-Profiler.html"&gt;sb-profile&lt;/a&gt; package to profile the functions involved in implementing &lt;b&gt;SOUNDEX&lt;/b&gt;, including some built-ins that it calls.  To profile a function, pass its name to &lt;b&gt;SB-PROFILE:PROFILE&lt;/b&gt;.  After exercising the code, call &lt;b&gt;SB-PROFILE:REPORT&lt;/b&gt;, which prints timing information.  Call &lt;b&gt;SB-PROFILE:RESET&lt;/b&gt; between runs unless you want the results to accumulate (we don't, in this case).&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;many-soundex&lt;/span&gt; ()&lt;br /&gt; (time&lt;br /&gt;  (&lt;span class="keyword"&gt;dotimes&lt;/span&gt; (i 100000)&lt;br /&gt;    (soundex &lt;span class="string"&gt;"supercalifrag"&lt;/span&gt;))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;profile-soundex&lt;/span&gt; ()&lt;br /&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;Don't accumulate results between runs.&lt;br /&gt;&lt;/span&gt;  (sb-profile:reset)&lt;br /&gt; &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;Calling this every time through in case any of the user-defined&lt;br /&gt;&lt;/span&gt;  &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;functions was recompiled.&lt;br /&gt;&lt;/span&gt;  (sb-profile:profile soundex soundex-tr uniq!&lt;br /&gt;                     concatenate make-string subseq&lt;br /&gt;                     string-left-trim delete-if-not&lt;br /&gt;                     tr delete string-upcase nsubseq)&lt;br /&gt; (many-soundex)&lt;br /&gt; (sb-profile:report))&lt;/pre&gt;If you notice some unfamiliar functions there, don't worry, they're going to be defined later; for the purposes of this blog I just want to define this function once, even though in real life I modified it many times.&lt;br /&gt;&lt;br /&gt;Let's see what it produces:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(profile-soundex)&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-output"&gt; 3.713 seconds of real time&lt;br /&gt; 3.160197 seconds of user run time&lt;br /&gt; 0.548034 seconds of system run time&lt;br /&gt; [Run times include 0.08 seconds GC run time.]&lt;br /&gt; 0 calls to %EVAL&lt;br /&gt; 0 page faults and&lt;br /&gt; 200,022,056 bytes consed.&lt;br /&gt; seconds  |    consed   |  calls  |  sec/call  |  name &lt;br /&gt;-----------------------------------------------------------&lt;br /&gt;    1.394 | 140,115,928 | 100,000 |   0.000014 | TR&lt;br /&gt;    0.618 |  26,230,456 | 100,000 |   0.000006 | CONCATENATE&lt;br /&gt;    0.210 |     661,840 | 100,000 |   0.000002 | REMOVE-IF-NOT&lt;br /&gt;    0.170 |   3,864,248 | 100,000 |   0.000002 | DELETE&lt;br /&gt;    0.164 |   9,865,784 | 100,000 |   0.000002 | SOUNDEX&lt;br /&gt;    0.131 |           0 | 100,000 |   0.000001 | UNIQ!&lt;br /&gt;    0.130 |   9,274,680 | 100,000 |   0.000001 | STRING-UPCASE&lt;br /&gt;    0.058 |  10,012,888 | 100,003 |   0.000001 | SUBSEQ&lt;br /&gt;    0.000 |           0 |       7 |   0.000000 | STRING-LEFT-TRIM&lt;br /&gt;-----------------------------------------------------------&lt;br /&gt;    2.878 | 200,025,824 | 800,010 |            | Total&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;This shows that we probably should optimize &lt;b&gt;TR&lt;/b&gt; for memory consumption first.  What's wrong with &lt;b&gt;TR&lt;/b&gt;?  Every call to &lt;b&gt;TR&lt;/b&gt; copies &lt;b&gt;*ASCII-TABLE*&lt;/b&gt; into the &lt;b&gt;TABLE&lt;/b&gt; local variable, which will later be used to map each character to a (potentially) different one.  The inefficiency is that &lt;b&gt;TABLE&lt;/b&gt; only varies based on the 2nd and 3rd arguments (&lt;b&gt;FROM-TABLE&lt;/b&gt; and &lt;b&gt;TO-TABLE&lt;/b&gt;).  Since &lt;b&gt;SOUNDEX&lt;/b&gt; &lt;i&gt;always&lt;/i&gt; passes in the same thing for those two arguments every time, it is wasteful to continually recreate the same &lt;b&gt;TABLE&lt;/b&gt;.  To fix it use a closure that "closes over" a single instance of &lt;b&gt;TABLE&lt;/b&gt;:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;make-tr-fn&lt;/span&gt; (from-table to-table)&lt;br /&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((table (copy-seq *ascii-table*)))&lt;br /&gt;   (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;      for from-char across from-table&lt;br /&gt;      and to-char across to-table&lt;br /&gt;      do (setf (aref table (char-code from-char)) to-char))&lt;br /&gt;   (&lt;span class="keyword"&gt;&lt;span class="slime-highlight"&gt;lambda&lt;/span&gt;&lt;/span&gt; (string)&lt;br /&gt;     (&lt;span class="keyword"&gt;declare&lt;/span&gt; ((simple-array character) string))&lt;br /&gt;     (map-into string&lt;br /&gt;               #'(&lt;span class="keyword"&gt;lambda&lt;/span&gt; (c) (aref table (char-code c)))&lt;br /&gt;               string))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defparameter&lt;/span&gt; &lt;span class="variable-name"&gt;*soundex-tr-fn*&lt;/span&gt; (make-tr-fn &lt;span class="string"&gt;"AEHIOUWYBFPVCGJKQSXZDTLMNR"&lt;/span&gt; &lt;span class="string"&gt;"00000000111122222222334556"&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex-tr&lt;/span&gt; (string)&lt;br /&gt; (funcall *soundex-tr-fn* string))&lt;/pre&gt;This is an example of the sort of rote transformations you often need to do in order to speed up performance.&lt;ol&gt;&lt;li&gt;Separate out the part of the function that varies dynamically into a &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/mac_lambda.html"&gt;LAMBDA&lt;/a&gt;&lt;/b&gt;.&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Return the &lt;b&gt;LAMBDA&lt;/b&gt; instead of the original value so that it can be reused over and over again.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;This is something you often do in Java, too, where the pattern goes:&lt;ol&gt;&lt;li&gt;Create a class.&lt;/li&gt;&lt;li&gt;Have the constructor do the stuff that only needs to happen once (equivalent to the part outside of the &lt;b&gt;LAMBDA&lt;/b&gt;.)&lt;/li&gt;&lt;li&gt;Create a method that does the dynamic part.&lt;/li&gt;&lt;/ol&gt;The main difference the CL and Java is that the Java would be more verbose.&lt;br /&gt;&lt;br /&gt;You may notice another difference between &lt;b&gt;MAKE-TR-FN&lt;/b&gt; and &lt;b&gt;TR&lt;/b&gt;; it's now calling &lt;b&gt;&lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_map_in.htm"&gt;MAP-INTO&lt;/a&gt;&lt;/b&gt; instead of &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_map.html"&gt;MAP&lt;/a&gt;&lt;/b&gt;, which avoids making a copy of the result, thus reducing the memory consumption further.  Now this particular optimization &lt;i&gt;does&lt;/i&gt; change the semantics of the function to modify its argument.  In this case it's ok because we've already made a copy of the string passed to &lt;b&gt;SOUNDEX&lt;/b&gt;, but  do not use this technique without thinking through the consequences.  Also, the &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/sym_declare.html"&gt;DECLARE&lt;/a&gt;&lt;/b&gt; statement fixes a minor compiler warning caused by the change.  &lt;b&gt;DECLARE&lt;/b&gt; is discussed later.&lt;br /&gt;&lt;br /&gt;When originally doing this work, I re-ran &lt;b&gt;PROFILE-SOUNDEX&lt;/b&gt; after making each change, to see if it helped.  That is not shown here in order to save space, but obviously you'd want to do the same thing.  Let's now move on to the next biggest offender in the list, &lt;b&gt;CONCATENATE&lt;/b&gt;.  It's called towards the end of &lt;b&gt;SOUNDEX&lt;/b&gt; in order to ensure that the return value is at least 4 characters (right-padded with '0' characters).  This was a direct port of the Perl code (or at least as direct as you can get without using CL-PPCRE), and it's inefficient.  Since the return value is &lt;i&gt;always&lt;/i&gt; a 4-character string, we need only allocate a fixed-size string (using &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_make-string.html"&gt;MAKE-STRING&lt;/a&gt;&lt;/b&gt;), prefilled with '0', and then copy in the (up to) 4 characters we need.  We no longer need &lt;b&gt;SUBSEQ&lt;/b&gt; which allocates a copy of its result (note: most of the memory consumed by &lt;b&gt;SUBSEQ&lt;/b&gt; comes from it being called by &lt;b&gt;UNIQ!&lt;/b&gt;).&lt;br /&gt;&lt;br /&gt;In fact, since &lt;b&gt;SUBSEQ&lt;/b&gt; is next on our list of memory hogs, let's find a way to fix it.  When &lt;b&gt;UNIQ!&lt;/b&gt; calls it, it's operating on a so-called "garbage" sequence (&lt;b&gt;S2&lt;/b&gt; from &lt;b&gt;SOUNDEX&lt;/b&gt;); i.e., an intermediate result that is not used outside of the bowels of &lt;b&gt;SOUNDEX&lt;/b&gt;.  As such, we &lt;i&gt;could&lt;/i&gt; modify it, so it is a shame to use &lt;b&gt;SUBSEQ&lt;/b&gt; on it within &lt;b&gt;UNIQ!&lt;/b&gt;.  CL has a loosely-followed convention that "destructive" (non-copying) version of functions begin with the letter &lt;i&gt;N&lt;/i&gt;.  There is no built-in &lt;b&gt;NSUBSEQ&lt;/b&gt; but a quick Google search finds one that works for our purposes:&lt;pre class="code"&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;From http://darcs.informatimago.com/lisp/common-lisp/utility.lisp&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;nsubseq&lt;/span&gt; (sequence start &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; (end nil))&lt;br /&gt; &lt;span class="doc"&gt;"&lt;br /&gt;RETURN:  When the SEQUENCE is a vector, the SEQUENCE itself, or a displaced&lt;br /&gt;        array to the SEQUENCE.&lt;br /&gt;        When the SEQUENCE is a list, it may destroy the list and reuse the&lt;br /&gt;        cons cells to make the subsequence.&lt;br /&gt;"&lt;/span&gt;&lt;br /&gt; (&lt;span class="keyword"&gt;if&lt;/span&gt; (vectorp sequence)&lt;br /&gt;     (&lt;span class="keyword"&gt;if&lt;/span&gt; (and (zerop start) (or (null end) (= end (length sequence))))&lt;br /&gt;         sequence&lt;br /&gt;         (make-array (- (&lt;span class="keyword"&gt;if&lt;/span&gt; end&lt;br /&gt;                            (min end (length sequence))&lt;br /&gt;                            (length sequence))&lt;br /&gt;                        start)&lt;br /&gt;                     &lt;span class="builtin"&gt;:element-type&lt;/span&gt; (array-element-type sequence)&lt;br /&gt;                     &lt;span class="builtin"&gt;:displaced-to&lt;/span&gt; sequence&lt;br /&gt;                     &lt;span class="builtin"&gt;:displaced-index-offset&lt;/span&gt; start))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((result (nthcdr start sequence)))&lt;br /&gt;       (&lt;span class="keyword"&gt;when&lt;/span&gt; end&lt;br /&gt;         (setf (cdr (nthcdr (- end start -1) sequence)) nil))&lt;br /&gt;       result)))&lt;/pre&gt;We'll call this from &lt;b&gt;UNIQ!&lt;/b&gt; and that should take care of that 10MB of allocations.  The last bit of memory we're going to take care of is that allocated by &lt;b&gt;STRING-UPCASE&lt;/b&gt; and &lt;b&gt;REMOVE-IF-NOT&lt;/b&gt;.  Now we have to be careful here because we &lt;i&gt;want&lt;/i&gt; one copy of the argument to &lt;b&gt;SOUNDEX&lt;/b&gt;; it would be rude to transform the caller's argument unexpectedly.  So we actually &lt;i&gt;relying&lt;/i&gt; on either &lt;b&gt;STRING-UPCASE&lt;/b&gt; or &lt;b&gt;REMOVE-IF-NOT&lt;/b&gt; making a copy of &lt;b&gt;STRING&lt;/b&gt;.  As such, we have to pick one to optimize.  Since &lt;b&gt;REMOVE-IF-NOT&lt;/b&gt; has a destructive equivalent, &lt;b&gt;DELETE-IF-NOT&lt;/b&gt;, we will use that instead.  Unfortunately, it's not as simple as just dropping in &lt;b&gt;DELETE-IF-NOT&lt;/b&gt;.  If you read the &lt;a href="http://www.lisp.org/HyperSpec/Body/fun_string-up_g-capitalize.html"&gt;CLHS entry&lt;/a&gt; you'll discover that &lt;b&gt;STRING-UPCASE&lt;/b&gt; is allowed to return the same string it was passed in (i.e., &lt;i&gt;not&lt;/i&gt; a copy) if it doesn't need to change it (e.g., all the letters are already uppercase).  We account for this by calling &lt;b&gt;REMOVE-IF-NOT&lt;/b&gt; in the case where &lt;b&gt;STRING-UPCASE&lt;/b&gt; does this by seeing if the return value is &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/fun_eq.html"&gt;EQ&lt;/a&gt;&lt;/b&gt; (same object) to the original string.  You can see this in the new code listing, which has certain key optimizations highlighted for your convenience:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defparameter&lt;/span&gt; &lt;span class="variable-name"&gt;*ascii-table*&lt;/span&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((table (make-array '(256) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'character)))&lt;br /&gt;                             (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;                                for i below 256&lt;br /&gt;                                do (setf (aref table i) (code-char i)))&lt;br /&gt;                             table))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;make-tr-fn&lt;/span&gt; (from-table to-table)&lt;br /&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((table (copy-seq *ascii-table*)))&lt;br /&gt;   (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;      for from-char across from-table&lt;br /&gt;      and to-char across to-table&lt;br /&gt;      do (setf (aref table (char-code from-char)) to-char))&lt;br /&gt;   (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (string)&lt;br /&gt;     (&lt;span class="keyword"&gt;declare&lt;/span&gt; ((simple-array character) string))&lt;br /&gt;     (map-into string&lt;br /&gt;               #'(&lt;span class="keyword"&gt;lambda&lt;/span&gt; (c) (aref table (char-code c)))&lt;br /&gt;               string))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defparameter&lt;/span&gt; &lt;span class="variable-name"&gt;*soundex-tr-fn*&lt;/span&gt; (make-tr-fn &lt;span class="string"&gt;"AEHIOUWYBFPVCGJKQSXZDTLMNR"&lt;/span&gt; &lt;span class="string"&gt;"00000000111122222222334556"&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex-tr&lt;/span&gt; (string)&lt;br /&gt; (funcall *soundex-tr-fn* string))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;uniq!&lt;/span&gt; (seq)&lt;br /&gt; (&lt;span class="keyword"&gt;cond&lt;/span&gt;&lt;br /&gt;   ((&amp;gt; (length seq) 1)&lt;br /&gt;    (&lt;span class="keyword"&gt;do*&lt;/span&gt; ((cur 0)&lt;br /&gt;          (cur-elt (elt seq cur) (elt seq cur))&lt;br /&gt;          (next 1 (1+ next)))&lt;br /&gt;         ((&amp;gt;= next (length seq)) (&lt;span class="slime-highlight"&gt;nsubseq&lt;/span&gt; seq 0 (1+ cur)))&lt;br /&gt;      (&lt;span class="keyword"&gt;let&lt;/span&gt; ((next-char (elt seq next)))&lt;br /&gt;        (&lt;span class="keyword"&gt;unless&lt;/span&gt; (eql cur-elt next-char)&lt;br /&gt;          (incf cur)&lt;br /&gt;          (setf (elt seq cur) next-char)))))&lt;br /&gt;   (t seq)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex&lt;/span&gt; (string)&lt;br /&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((s (&lt;span class="keyword"&gt;let&lt;/span&gt; ((maybe-a-copy (string-upcase string)))&lt;br /&gt;            &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;STRING-UPCASE can return original string if no changes&lt;br /&gt;&lt;/span&gt;             &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;were needed.&lt;br /&gt;&lt;/span&gt;             (&lt;span class="keyword"&gt;if&lt;/span&gt; (&lt;span class="slime-highlight"&gt;eq&lt;/span&gt; maybe-a-copy string)&lt;br /&gt;                &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;REMOVE-IF-NOT makes a copy&lt;br /&gt;&lt;/span&gt;                 (remove-if-not 'alpha-char-p maybe-a-copy)&lt;br /&gt;                &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;DELETE-IF-NOT doesn't&lt;br /&gt;&lt;/span&gt;                 (&lt;span class="slime-highlight"&gt;delete-if-not&lt;/span&gt; 'alpha-char-p maybe-a-copy)))))&lt;br /&gt;   (&lt;span class="keyword"&gt;when&lt;/span&gt; (plusp (length s))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((f (char s 0)))&lt;br /&gt;       (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((s2 (soundex-tr s))&lt;br /&gt;              (fc (char s2 0))&lt;br /&gt;              (result (&lt;span class="slime-highlight"&gt;make-string&lt;/span&gt; 4 &lt;span class="builtin"&gt;:initial-element&lt;/span&gt; #\0)))&lt;br /&gt;         (setf s2 (delete #\0 (uniq! (string-left-trim (vector fc) s2))))&lt;br /&gt;         (setf (char result 0) f)&lt;br /&gt;         (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;            for i from 0 below (min (length s2) 4)&lt;br /&gt;            do (setf (char result (1+ i)) (char s2 i)))&lt;br /&gt;         result)))))&lt;br /&gt;&lt;/pre&gt;As mentioned above, in the course of actually doing this work I ran &lt;b&gt;PROFILE-SOUNDEX&lt;/b&gt; many times, but I don't show the intermediate results here in order to save space.  Now that we've completed a major chunk of work, however, let's see how we're doing:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(many-soundex)&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;&lt;span class="slime-repl-output"&gt;Evaluation took:&lt;br /&gt; 0.464 seconds of real time&lt;br /&gt; 0.460029 seconds of user run time&lt;br /&gt; 0.0 seconds of system run time&lt;br /&gt; [Run times include 0.008 seconds GC run time.]&lt;br /&gt; 0 calls to %EVAL&lt;br /&gt; 0 page faults and&lt;br /&gt; 22,402,176 bytes consed.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;We run &lt;b&gt;MANY-SOUNDEX&lt;/b&gt; here in a fresh instance of SBCL to remove any injected profiling code or other stuff that might affect the results.  It also has speed optimizations turned on.  As you can see, the benefit of simply removing memory allocations is significant.  The new code is a whopping 4.4 times faster!  It's also now more than twice as fast as the Perl code.&lt;br /&gt;&lt;br /&gt;We could continue to to whittle away at the memory allocation, as you can see we're still at around 21MB of memory allocated.  However, since we have to copy the argument to &lt;b&gt;SOUNDEX&lt;/b&gt; no matter what, 21MB is only about twice the minimum (if you look at the profiling results, we can't do better than the memory allocated by &lt;b&gt;STRING-UPCASE&lt;/b&gt;).  To keep this blog entry to a reasonable length we shall deem this acceptable.&lt;br /&gt;&lt;br /&gt;On to CPU optimization.  SBCL automatically detects certain performance problems when you up &lt;b&gt;SPEED&lt;/b&gt; to 3.  Fix these first, as it makes little sense to manually search out problems when the compiler has already found some.  Using SLIME makes it easy:&lt;ol&gt;&lt;li&gt;Put &lt;pre class="code"&gt;(&lt;span class="keyword"&gt;declaim&lt;/span&gt; (optimize (speed 3) (debug 0) (safety 0)))&lt;/pre&gt; at the top of the file.&lt;/li&gt;&lt;li&gt;Type &lt;b&gt;C-c C-k&lt;/b&gt; to compile the file.&lt;/li&gt;&lt;li&gt;Hit &lt;b&gt;M-n&lt;/b&gt; and &lt;b&gt;M-p&lt;/b&gt; to get SLIME to highlight the next (or previous) compiler warning.&lt;/li&gt;&lt;/ol&gt;I won't go through all of the compiler warnings I got when I did this, but instead will highlight some of them.&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex-tr&lt;/span&gt; (string)&lt;br /&gt; &lt;span class="slime-note"&gt;(funcall *soundex-tr-fn* string)&lt;/span&gt;)&lt;br /&gt;&lt;div class="codepop"&gt;; note: unable to&lt;br /&gt;;   optimize away possible call to FDEFINITION at runtime&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a (OR FUNCTION SYMBOL), not a FUNCTION.&lt;/div&gt;&lt;/pre&gt;This example seems a little strange at first, if, say, you're used to primitive type systems such as Java's.  CL types can be defined in sophisticated ways that deserve a blog entry of their own.  In this particular case, the compiler has inferred the type of &lt;b&gt;*SOUNDEX-TR-FN*&lt;/b&gt; to be &lt;b&gt;(OR FUNCTION SYMBOL)&lt;/b&gt;, meaning it isn't sure if it could sometimes be null (figuring that out would require "global" analysis to ensure that &lt;b&gt;*SOUNDEX-TR-FN*&lt;/b&gt; is never modified by any function anywhere, which is still, apparently, beyond the scope of most compilers).  We can fix the warning with a &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/speope_the.html"&gt;THE&lt;/a&gt;&lt;/b&gt; expression.  &lt;b&gt;THE&lt;/b&gt; is one of CL's ways of adding manual static typing (which you may be familiar with from more primitive languages such as Java) into your code.  Although CL implementations such as SBCL perform &lt;a href="http://en.wikipedia.org/wiki/Type_inference"&gt;type inference&lt;/a&gt;, the compiler occasionally needs your help:&lt;pre class="code"&gt;(funcall (&lt;span class="keyword"&gt;the&lt;/span&gt; function *soundex-tr-fn*) ...)&lt;/pre&gt;Since we're sure that &lt;b&gt;*SOUNDEX-TR-FN*&lt;/b&gt; is effectively constant (it's all private code under our own control, after all), it is safe to add in this &lt;b&gt;THE&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Another interesting set of warnings comes from &lt;b&gt;NSUBSEQ&lt;/b&gt;:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;nsubseq&lt;/span&gt; (sequence start &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; (end nil))&lt;br /&gt; (&lt;span class="keyword"&gt;if&lt;/span&gt; (vectorp sequence)&lt;br /&gt;     (&lt;span class="keyword"&gt;if&lt;/span&gt; (and &lt;span class="slime-note"&gt;(zerop start)&lt;/span&gt; (or (null end) &lt;span class="slime-note"&gt;(= end (length sequence))&lt;/span&gt;))&lt;br /&gt;         sequence&lt;br /&gt;         (make-array (&lt;span class="slime-note"&gt;-&lt;/span&gt; (&lt;span class="keyword"&gt;if&lt;/span&gt; end&lt;br /&gt;                            &lt;span class="slime-note"&gt;(min end (length sequence))&lt;/span&gt;&lt;br /&gt;                            (length sequence))&lt;br /&gt;                        start)&lt;br /&gt;                     &lt;span class="builtin"&gt;:element-type&lt;/span&gt; (array-element-type sequence)&lt;br /&gt;                     &lt;span class="builtin"&gt;:displaced-to&lt;/span&gt; sequence&lt;br /&gt;                     &lt;span class="builtin"&gt;:displaced-index-offset&lt;/span&gt; start))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((result (nthcdr start sequence)))&lt;br /&gt;       (&lt;span class="keyword"&gt;when&lt;/span&gt; end&lt;br /&gt;         (setf (cdr (nthcdr &lt;span class="slime-note"&gt;(- end start -1)&lt;/span&gt; sequence)) nil))&lt;br /&gt;       result)))&lt;div class="codepop"&gt;; in: DEFUN NSUBSEQ&lt;br /&gt;;     (ZEROP START)&lt;br /&gt;; ==&gt;&lt;br /&gt;;   (= START 0)&lt;br /&gt;;&lt;br /&gt;; note: unable to&lt;br /&gt;;   open-code FLOAT to RATIONAL comparison&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a NUMBER, not a FLOAT.&lt;br /&gt;;&lt;br /&gt;; note: unable to&lt;br /&gt;;   optimize&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a NUMBER, not a (COMPLEX SINGLE-FLOAT).&lt;br /&gt;;&lt;br /&gt;; note: unable to&lt;br /&gt;;   optimize&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a NUMBER, not a (COMPLEX DOUBLE-FLOAT).&lt;br /&gt;...blah blah blah...&lt;/div&gt;&lt;/pre&gt;Those warnings go on for a while.  What they all boil down to, though, is that the compiler doesn't know the type of &lt;b&gt;START&lt;/b&gt; or &lt;b&gt;END&lt;/b&gt;.  Now here is a key point.  Should you find yourself in this sort of situation, always remember what the great computer scientist Igor Stravinsky once said, "Lesser artists borrow, great artists steal."  In other words, don't try to figure out the type declarations; steal them from SBCL.  Using SLIME makes this easy.  Just type &lt;b&gt;M-.&lt;/b&gt; and then type &lt;b&gt;SUBSEQ&lt;/b&gt;, because obviously it should take the same parameters.  This will take you to the source code (you should always have the SBCL source code handy) for &lt;b&gt;SUBSEQ&lt;/b&gt;:&lt;pre class="code"&gt;&lt;span class="secondary-selection"&gt;(&lt;/span&gt;&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;subseq&lt;/span&gt; (sequence start &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; end)&lt;br /&gt; #!+sb-doc&lt;br /&gt; &lt;span class="string"&gt;"Return a copy of a subsequence of SEQUENCE starting with element number&lt;br /&gt;  START and continuing to the end of SEQUENCE or the optional END."&lt;/span&gt;&lt;br /&gt; (seq-dispatch sequence&lt;br /&gt;   (list-subseq* sequence start end)&lt;br /&gt;   (vector-subseq* sequence start end)&lt;br /&gt;   (sb!sequence:subseq sequence start end))&lt;span class="secondary-selection"&gt;)&lt;/span&gt;&lt;/pre&gt;Since we only care about the vector case here, move the cursor to &lt;b&gt;VECTOR-SUBSEQ*&lt;/b&gt; and hit &lt;b&gt;M-.&lt;/b&gt; again, to see how it declares its arguments:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;vector-subseq*&lt;/span&gt; (sequence start end)&lt;br /&gt; (&lt;span class="keyword"&gt;declare&lt;/span&gt; (type vector sequence))&lt;br /&gt; (&lt;span class="keyword"&gt;declare&lt;/span&gt; (type index start)&lt;br /&gt;          (type (or null index) end))&lt;br /&gt; ;; blah blah blah&lt;/pre&gt;Ah!  So what is the type &lt;b&gt;INDEX&lt;/b&gt;?  Use &lt;b&gt;M-.&lt;/b&gt; once more and you'll discover this:&lt;pre class="code"&gt;(def!type index () `(integer 0 (,sb!xc:array-dimension-limit)))&lt;/pre&gt;This makes sense.  The type obviously has to be an integer, cannot be negative, and cannot be more than the maximum array length allowed by your CL implementation.  This particular type definition is internal to SBCL and cannot be used directly, but we create our own.  Improved &lt;b&gt;NSUBSEQ&lt;/b&gt; looks like:&lt;pre class="code"&gt;(&lt;span class="slime-highlight"&gt;&lt;span class="keyword"&gt;deftype&lt;/span&gt; &lt;span class="type"&gt;index&lt;/span&gt;&lt;/span&gt; () `(integer 0 ,array-dimension-limit))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;From http://darcs.informatimago.com/lisp/common-lisp/utility.lisp&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;nsubseq&lt;/span&gt; (sequence start &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; (end nil))&lt;br /&gt; (&lt;span class="keyword"&gt;if&lt;/span&gt; (vectorp sequence)&lt;br /&gt;     (&lt;span class="slime-highlight"&gt;&lt;span class="keyword"&gt;locally&lt;/span&gt;&lt;br /&gt;         (&lt;span class="keyword"&gt;declare&lt;/span&gt; (index start)&lt;br /&gt;                  ((or null index) end))&lt;/span&gt;&lt;br /&gt;       (&lt;span class="keyword"&gt;if&lt;/span&gt; (and (zerop start) (or (null end) (= end (length sequence))))&lt;br /&gt;           sequence&lt;br /&gt;           (make-array (- (&lt;span class="keyword"&gt;if&lt;/span&gt; end&lt;br /&gt;                              (min end (length sequence))&lt;br /&gt;                              (length sequence))&lt;br /&gt;                          start)&lt;br /&gt;                       &lt;span class="builtin"&gt;:element-type&lt;/span&gt; (array-element-type sequence)&lt;br /&gt;                       &lt;span class="builtin"&gt;:displaced-to&lt;/span&gt; sequence&lt;br /&gt;                       &lt;span class="builtin"&gt;:displaced-index-offset&lt;/span&gt; start)))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((result (nthcdr start sequence)))&lt;br /&gt;       (&lt;span class="keyword"&gt;when&lt;/span&gt; end&lt;br /&gt;         (setf (cdr (nthcdr (- end start -1) sequence)) nil))&lt;br /&gt;       result)))&lt;/pre&gt;Note we use &lt;a href="http://www.lisp.org/HyperSpec/Body/speope_locally.html"&gt;&lt;b&gt;LOCALLY&lt;/b&gt;&lt;/a&gt; only when the argument is actually a vector; we can't make the same assumptions about other types of sequences.  Plus we don't care about the list case (in fact a compiler warning for that case remains, but since we're not using lists in this example we'll skip the fix for it).&lt;br /&gt;&lt;br /&gt;Let's look at optimizing &lt;b&gt;UNIQ!&lt;/b&gt;.  When we compile it, we get a number of warnings such as:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;uniq!&lt;/span&gt; (seq)&lt;br /&gt; (&lt;span class="keyword"&gt;cond&lt;/span&gt;&lt;br /&gt;   ((&amp;gt; &lt;span class="slime-note"&gt;(length seq)&lt;/span&gt; 1)&lt;br /&gt;    (&lt;span class="keyword"&gt;do*&lt;/span&gt; ((cur 0)&lt;br /&gt;          (cur-elt &lt;span class="slime-note"&gt;(elt seq cur)&lt;/span&gt; &lt;span class="slime-note"&gt;(elt seq cur)&lt;/span&gt;)&lt;br /&gt;          (next 1 (1+ next)))&lt;br /&gt;         ((&amp;gt;= next &lt;span class="slime-note"&gt;(length seq)&lt;/span&gt;) (nsubseq seq 0 (1+ cur)))&lt;br /&gt;      (&lt;span class="keyword"&gt;let&lt;/span&gt; ((next-char &lt;span class="slime-note"&gt;(elt seq next)&lt;/span&gt;))&lt;br /&gt;        (&lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="slime-note"&gt;(eql cur-elt next-char)&lt;/span&gt;&lt;br /&gt;          (incf cur)&lt;br /&gt;          &lt;span class="slime-note"&gt;(setf (elt seq cur) next-char)&lt;/span&gt;))))&lt;br /&gt;   (t seq)))&lt;div class="codepop"&gt;; in: DEFUN UNIQ!&lt;br /&gt;;     (LENGTH SEQ)&lt;br /&gt;;&lt;br /&gt;; note: unable to&lt;br /&gt;;   optimize&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a SEQUENCE, not a (SIMPLE-ARRAY * (*)).&lt;br /&gt;;&lt;br /&gt;; note: unable to&lt;br /&gt;;   optimize&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a SEQUENCE, not a VECTOR.&lt;br /&gt;&lt;br /&gt;;     (ELT SEQ CUR)&lt;br /&gt;;&lt;br /&gt;; note: unable to&lt;br /&gt;;   optimize&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a SEQUENCE, not a (SIMPLE-ARRAY * (*)).&lt;br /&gt;...blah blah blah...&lt;/div&gt;&lt;/pre&gt;Here the compiler is telling us that it could make some optimizations if it knew for sure that the &lt;b&gt;SEQ&lt;/b&gt; were a &lt;b&gt;&lt;a href="http://www.lispworks.com/documentation/HyperSpec/Body/t_smp_ar.htm"&gt;SIMPLE-ARRAY&lt;/a&gt;&lt;/b&gt;.  What the heck is a simple array?  Clicking link (or using &lt;b&gt;C-c C-d h&lt;/b&gt; in SLIME), shows: &lt;blockquote&gt;The type of an array that is not displaced to another array, has no fill pointer, and is not&lt;br /&gt;expressly adjustable is a subtype of type simple-array. The concept of a simple array exists to&lt;br /&gt;allow the implementation to use a specialized representation and to allow the user to declare that&lt;br /&gt;certain values will always be simple arrays.&lt;/blockquote&gt;In case you're wondering, "displaced" means an array slice (as in Perl or Python), and the other two concepts refer to various ways an array can be (or appear to be) extensible.  Is it safe for &lt;b&gt;UNIQ!&lt;/b&gt; to assume it is receiving a simple array?  Yes; its input comes from &lt;b&gt;STRING-LEFT-TRIM&lt;/b&gt;.  Although &lt;b&gt;STRING-LEFT-TRIM&lt;/b&gt; &lt;i&gt;is&lt;/i&gt; allowed to return its input when there are no changes to be made, we know that it will &lt;i&gt;always&lt;/i&gt; make a change because we always remove at least the first character of &lt;b&gt;S2&lt;/b&gt;, and thus its return value will always be simple.  So let's add a function declaration for &lt;b&gt;UNIQ!&lt;/b&gt;:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;declaim&lt;/span&gt; (ftype (function (simple-array) string) uniq!))&lt;/pre&gt;This tells the compiler that &lt;b&gt;UNIQ!&lt;/b&gt;'s single argument is a &lt;b&gt;SIMPLE-ARRAY&lt;/b&gt; and that it returns a &lt;b&gt;STRING&lt;/b&gt;.  The reason we say it returns a &lt;b&gt;STRING&lt;/b&gt; (instead of a "simple" sequence type) is that &lt;b&gt;UNIQ!&lt;/b&gt; uses &lt;b&gt;NSUBSEQ&lt;/b&gt;, which &lt;i&gt;does&lt;/i&gt; return an array slice.  Unfortunately, we still get compiler warnings after making this change (albeit fewer):&lt;pre class="code"&gt;             (cur-elt &lt;span class="slime-note"&gt;(elt seq cur)&lt;/span&gt; &lt;span class="slime-note"&gt;(elt seq cur)&lt;/span&gt;)&lt;div class="codepop"&gt;; note: unable to&lt;br /&gt;;   optimize&lt;br /&gt;; due to type uncertainty:&lt;br /&gt;;   The first argument is a (SIMPLE-ARRAY * (*)), not a SIMPLE-STRING.&lt;br /&gt;...blah blah blah...&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;This is telling us that it could generate better code if it knew the sequence was a string.  &lt;b&gt;&lt;a href="http://www.lisp.org/HyperSpec/Body/acc_elt.html"&gt;ELT&lt;/a&gt;&lt;/b&gt; is a generic accessor that works on any sequence type; presumably if the compiler knows the sequence type is a string it can take advantage of the fact that each element is the same size.  Anyway, this is easy to fix.  &lt;b&gt;SOUNDEX&lt;/b&gt; only deals in strings, so we can safely change the declaration from &lt;b&gt;SIMPLE-ARRAY&lt;/b&gt; to &lt;b&gt;SIMPLE-STRING&lt;/b&gt;.  Sure enough, this makes the compiler warnings disappear.&lt;br /&gt;&lt;br /&gt;The remaining changes made were generally of the same nature as the above so are not covered here in detail, but you can see them in the following complete code listing that highlights all the CPU-related changes:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;declaim&lt;/span&gt; (optimize (speed 3) (debug 0) (safety 0)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defparameter&lt;/span&gt; &lt;span class="variable-name"&gt;*ascii-table*&lt;/span&gt; (&lt;span class="keyword"&gt;let&lt;/span&gt; ((table (make-array '(256) &lt;span class="builtin"&gt;:element-type&lt;/span&gt; 'character)))&lt;br /&gt;                             (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;                                for i below 256&lt;br /&gt;                                do (setf (aref table i) (code-char i)))&lt;br /&gt;                             table))&lt;br /&gt;&lt;span class="slime-highlight"&gt;&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;make-tr-fn&lt;/span&gt; (from-table to-table)&lt;br /&gt;&lt;span class="slime-highlight"&gt;  (declare (simple-string from-table to-table)&lt;br /&gt;          (simple-array *ascii-table*))&lt;br /&gt; (let ((table (the (simple-array character) (copy-seq *ascii-table*))))&lt;br /&gt;&lt;/span&gt;    (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;      for from-char across from-table&lt;br /&gt;      and to-char across to-table&lt;br /&gt;      do (setf (aref table (char-code from-char)) to-char))&lt;br /&gt;   (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (string)&lt;br /&gt;     (&lt;span class="keyword"&gt;declare&lt;/span&gt; ((simple-array character) string))&lt;br /&gt;     (map-into string&lt;br /&gt;               #'(&lt;span class="keyword"&gt;lambda&lt;/span&gt; (c) (aref table (char-code c)))&lt;br /&gt;               string))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defparameter&lt;/span&gt; &lt;span class="variable-name"&gt;*soundex-tr-fn*&lt;/span&gt; (make-tr-fn &lt;span class="string"&gt;"AEHIOUWYBFPVCGJKQSXZDTLMNR"&lt;/span&gt; &lt;span class="string"&gt;"00000000111122222222334556"&lt;/span&gt;))&lt;br /&gt;&lt;br /&gt;&lt;span class="slime-highlight"&gt;(declaim (ftype (function (simple-string) simple-string) soundex-tr))&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex-tr&lt;/span&gt; (string)&lt;br /&gt;&lt;span class="slime-highlight"&gt;  (funcall (the function *soundex-tr-fn*) string))&lt;br /&gt;&lt;br /&gt;(deftype index () `(integer 0 ,array-dimension-limit))&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;From http://darcs.informatimago.com/lisp/common-lisp/utility.lisp&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;nsubseq&lt;/span&gt; (sequence start &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; (end nil))&lt;br /&gt; &lt;span class="doc"&gt;"&lt;br /&gt;RETURN:  When the SEQUENCE is a vector, the SEQUENCE itself, or a displaced&lt;br /&gt;        array to the SEQUENCE.&lt;br /&gt;        When the SEQUENCE is a list, it may destroy the list and reuse the&lt;br /&gt;        cons cells to make the subsequence.&lt;br /&gt;"&lt;/span&gt;&lt;br /&gt; (&lt;span class="keyword"&gt;if&lt;/span&gt; (vectorp sequence)&lt;br /&gt;&lt;span class="slime-highlight"&gt;      (locally&lt;br /&gt;         (declare (index start)&lt;br /&gt;                  ((or null index) end))&lt;/span&gt;&lt;br /&gt;       (if (and (zerop start) (or (null end) (= end (length sequence))))&lt;br /&gt;           sequence&lt;br /&gt;           (make-array (- (if end&lt;br /&gt;                              (min end (length sequence))&lt;br /&gt;                              (length sequence))&lt;br /&gt;                          start)&lt;br /&gt;                       :element-type (array-element-type sequence)&lt;br /&gt;                       :displaced-to sequence&lt;br /&gt;                       :displaced-index-offset start)))&lt;br /&gt;      (&lt;span class="keyword"&gt;let&lt;/span&gt; ((result (nthcdr start sequence)))&lt;br /&gt;       (&lt;span class="keyword"&gt;when&lt;/span&gt; end&lt;br /&gt;         (setf (cdr (nthcdr (- end start -1) sequence)) nil))&lt;br /&gt;       result)))&lt;br /&gt;&lt;br /&gt;&lt;span class="slime-highlight"&gt;(declaim (ftype (function (simple-string) string) uniq!))&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;uniq!&lt;/span&gt; (seq)&lt;br /&gt;&lt;span class="slime-highlight"&gt;  (let ((seq-len (length seq)))&lt;/span&gt;&lt;br /&gt;   (cond&lt;br /&gt;     ((&amp;gt; seq-len 1)&lt;br /&gt;      (do* ((cur 0)&lt;br /&gt;            (cur-elt (elt seq cur) (elt seq cur))&lt;br /&gt;            (next 1 (1+ next)))&lt;br /&gt;           ((&amp;gt;= next &lt;span class="slime-highlight"&gt;seq-len&lt;/span&gt;) (nsubseq seq 0 (1+ cur)))&lt;br /&gt;        (let ((next-char (elt seq next)))&lt;br /&gt;          (unless (eql cur-elt next-char)&lt;br /&gt;            (incf cur)&lt;br /&gt;            (setf (elt seq cur) next-char)))))&lt;br /&gt;     (t seq))))&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;soundex&lt;/span&gt; (string)&lt;br /&gt;  (let ((s &lt;span class="slime-highlight"&gt;(the simple-string&lt;/span&gt;&lt;br /&gt;            (let ((maybe-a-copy (string-upcase string)))&lt;br /&gt;              ;; STRING-UPCASE can return original string if no changes&lt;br /&gt;              ;; were needed.&lt;br /&gt;              (if (eq maybe-a-copy string)&lt;br /&gt;                  ;; REMOVE-IF-NOT makes a copy&lt;br /&gt;                  (remove-if-not 'alpha-char-p maybe-a-copy)&lt;br /&gt;                  ;; DELETE-IF-NOT doesn't&lt;br /&gt;                  (delete-if-not 'alpha-char-p maybe-a-copy))))))&lt;br /&gt;    (&lt;span class="keyword"&gt;when&lt;/span&gt; (plusp (length s))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((f (char s 0)))&lt;br /&gt;       (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((s2 (soundex-tr s))&lt;br /&gt;              (fc (char s2 0))&lt;br /&gt;              (result (make-string 4 &lt;span class="builtin"&gt;:initial-element&lt;/span&gt; #\0)))&lt;br /&gt;          (setf s2 &lt;span class="slime-highlight"&gt;(the string&lt;/span&gt; (delete #\0 (uniq! (string-left-trim (vector fc) s2)))))&lt;br /&gt;          (setf (char result 0) f)&lt;br /&gt;&lt;span class="slime-highlight"&gt;          (let ((end (min (length s2) 4)))&lt;/span&gt;&lt;br /&gt;           (loop&lt;br /&gt;              for i from 0 below &lt;span class="slime-highlight"&gt;end&lt;/span&gt;&lt;br /&gt;              do (setf (char result (1+ i)) (char s2 i))))&lt;br /&gt;          result)))))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here's the effect of the CPU optimizations:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt; &lt;/span&gt;&lt;span class="slime-repl-input"&gt;(many-soundex)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-output"&gt;Evaluation took:&lt;br /&gt; 0.357 seconds of real time&lt;br /&gt; 0.360022 seconds of user run time&lt;br /&gt; 0.0 seconds of system run time&lt;br /&gt; [Run times include 0.008 seconds GC run time.]&lt;br /&gt; 0 calls to %EVAL&lt;br /&gt; 0 page faults and&lt;br /&gt; 22,406,384 bytes consed.&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;Although the results vary from run to run, 0.357 seconds is a pretty typical on my hardware.  This is approximately a 23% reduction in time compared to before.  Not nearly as big of a gain, which is why one should optimize memory first.  Still, 23% is nothing to sneeze at for a relatively small number of changes, most of which were pointed out to us by the compiler!  If you're interested in learning more about optimizing CL, I recommend checking out SBCL's &lt;b&gt;SB-SPROF&lt;/b&gt; package, which is more sophisticated than the &lt;b&gt;SB-PROFILE&lt;/b&gt; package used here.&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/JxlQs03NOMY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/6339151957936058494/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=6339151957936058494" title="14 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6339151957936058494?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6339151957936058494?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/JxlQs03NOMY/optimizing-cl.html" title="Optimizing CL" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>14</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2008/03/optimizing-cl.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04MQ3g-cSp7ImA9WxZVGEw.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-7915719393787254012</id><published>2008-02-12T21:29:00.000-08:00</published><updated>2008-03-29T12:46:22.659-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-29T12:46:22.659-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="lisp" /><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><title>Scripting in CL</title><content type="html">&lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;CL&lt;/a&gt; has been overlooked as a scripting language, even though scripting in CL is faster than it is in traditional scripting languages.  CL's interactivity and macro system make it ideal for quick-and-dirty hacking.&lt;br /&gt;&lt;br /&gt;For one thing, its syntax is so simple that almost anything can be typed in at the CL command-line:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;23&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;23&lt;/div&gt;&lt;/pre&gt;That's a valid CL "program".  It returns 23.  So say that you forgot how part of the language works; resolve the problem by typing some test code at the command-line without breaking your concentration.  For example, say you forget how division works in CL; you may have forgotten whether CL has support for rationals.  Don't look it up, just try it out immediately:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(/ 23 45)&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;23/45&lt;/div&gt;&lt;/pre&gt;Sure enough, it doesn't return a float!  This interactivity isn't just useful for exploring (or remembering) basic language features.  It's also how you script in CL. &lt;br /&gt;&lt;br /&gt;For example, here is how to write a CL version of the popular Unix utility &lt;a href="http://en.wikipedia.org/wiki/Grep"&gt;grep&lt;/a&gt;, which you might then use as the basis for a log analyzer or some other script.  Start by working interactively on a small part of the problem, such as how to see if a string contains a regexp.  Of course our goal is to pick lines out of a file, but let's ignore the file part for now, we have to walk before we run.  First define some sample data:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(defparameter *lines* "hi there&lt;br /&gt;my name is bob&lt;br /&gt;I live in Kalamazoo")&lt;/span&gt;&lt;/pre&gt;Now google for how to use regular expressions in CL.  All roads lead to &lt;a href="http://www.weitz.de/cl-ppcre/"&gt;CL-PPCRE&lt;/a&gt;.  To install it, do this:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(require :asdf-install)&lt;/span&gt; ; if you haven't already&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(asdf-install:install :cl-ppcre)&lt;/span&gt; ; if you don't have it already&lt;br /&gt;&lt;/pre&gt;To use it, do this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(require :cl-ppcre)&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(use-package :cl-ppcre)&lt;/span&gt; ; to save typing cl-ppcre: a lot&lt;br /&gt;&lt;/pre&gt;Next use &lt;a href="http://common-lisp.net/project/slime/"&gt;SLIME&lt;/a&gt;'s autocomplete to figure out what functions are available:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(cl-ppcre:&lt;span class="slime-highlight"&gt;&amp;lt;TAB&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;Flags: boundp fboundp generic-function class macro special-operator package&lt;br /&gt;&lt;br /&gt;Completion:                        Flags:  Score:&lt;br /&gt;---------------------------------- ------- --------&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:                          b------    98.44&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;-test:                     b------    90.11&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:*allow-named-registers*   b------     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:*allow-quoting*           b------     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:*regex-char-code-limit*   b------     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:*use-bmh-matchers*        b------     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:all-matches               -f-----     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:all-matches-as-strings    -f-----     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:create-scanner            -fg----     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:define-parse-tree-synonym -f--m--     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:do-matches                -f--m--     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:do-matches-as-strings     -f--m--     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:do-register-groups        -f--m--     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:do-scans                  -f--m--     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:parse-tree-synonym        -f-----     0.98&lt;br /&gt;&lt;span class="bold"&gt;cl-ppcre&lt;/span&gt;:ppcre-error               ---c---     0.98&lt;br /&gt;...etc...&lt;br /&gt;&lt;/div&gt;&lt;/pre&gt;Although this technique may seem haphazard, in practice one finds what one wants in about a minute or two (at most), typically without losing one's concentration.  Most of the functions can be trivially filtered out simply based on their names (e.g., &lt;b&gt;PARSE-TREE-SYNONYM&lt;/b&gt; is obviously not what I'm looking for).  Of the rest, you can either quickly check their documentation (&lt;span style="font-weight:bold;"&gt;C-c C-d d&lt;/span&gt; if you're using SLIME) or else, heck, just try them out immediately.  In this particular case I settled on &lt;b&gt;SCAN&lt;/b&gt;.&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(scan "Kalamazoo" *line*)&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;34&lt;br /&gt;43&lt;br /&gt;#()&lt;br /&gt;#()&lt;/div&gt;&lt;/pre&gt;It doesn't matter what those return values mean (probably the offset of the match is in there somewhere), the key point is that &lt;span style="font-weight:bold;"&gt;SCAN&lt;/span&gt; would have returned &lt;span style="font-weight:bold;"&gt;NIL&lt;/span&gt; if there hadn't been a match.  Or so we hope.  We had better verify that (quickly; &lt;i&gt;don't look at the documentation&lt;/i&gt;, just try it!):&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(scan "snord" *line*)&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;NIL&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;Ok, now how do we read in a line from a file?  Well, first we have to open the file:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(open "/etc/motd")&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;#&amp;lt;SB-SYS:FD-STREAM for "file /etc/motd" {A78C2E9}&amp;gt;&lt;/div&gt;&lt;/pre&gt;That looks promising.  A little more research on Google shows that it's best to use &lt;a href="http://www.lisp.org/HyperSpec/Body/mac_with-open-file.html"&gt;WITH-OPEN-FILE&lt;/a&gt; because that will automatically close the file (even if an exception is thrown).&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(with-open-file (stream "/etc/motd"))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;NIL&lt;/div&gt;&lt;/pre&gt;How do you read a line from a file?  Turns out there's a function &lt;a href="http://www.lisp.org/HyperSpec/Body/fun_read-line.html"&gt;READ-LINE&lt;/a&gt;:&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(with-open-file (stream "/etc/motd") (read-line stream nil nil))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;"Linux D830 2.6.22-14-generic #1 SMP Fri Feb 1 04:59:50 UTC 2008 i686"&lt;/div&gt;&lt;/pre&gt;Ok, so we can read lines from a file, now what?  Well, let's play around with the &lt;a href="http://www.gigamonkeys.com/book/loop-for-black-belts.html"&gt;LOOP&lt;/a&gt; macro to print out &lt;span style="font-style:italic;"&gt;all&lt;/span&gt; the lines of the file (don't worry about the regexp matching just yet):&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(defparameter *motd* (open "/etc/motd"))&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;*MOTD*&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(loop for line = (read-line *motd* nil nil)&lt;br /&gt;      while line&lt;br /&gt;      do (format t "~A~%" line))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;Linux D830 2.6.22-14-generic #1 SMP Fri Feb 1 04:59:50 UTC 2008 i686&lt;br /&gt;&lt;br /&gt;The programs included with the Ubuntu system are free software;&lt;br /&gt;the exact distribution terms for each program are described in the&lt;br /&gt;individual files in /usr/share/doc/*/copyright.&lt;br /&gt;&lt;br /&gt;Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by&lt;br /&gt;applicable law.&lt;/div&gt;&lt;/pre&gt;Ok, that worked.  Now let's scan for all the lines that contain the word "Ubuntu".&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(defparameter *motd* (open "/etc/motd"))&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-result"&gt;*MOTD*&lt;/span&gt;&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(loop for line = (read-line *motd* nil nil)&lt;br /&gt;             while line&lt;br /&gt;             do (when (scan "Ubuntu" line)&lt;br /&gt;                  (format t "~A~%" line)))&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;The programs included with the Ubuntu system are free software;&lt;br /&gt;Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by&lt;/div&gt;&lt;/pre&gt;Ok, that looks good.  Now let's write a function with everything we just did:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;grep&lt;/span&gt; (regexp filename)&lt;br /&gt;  (&lt;span class="keyword"&gt;with-open-file&lt;/span&gt; (stream filename)&lt;br /&gt;                  (&lt;span class="keyword"&gt;loop&lt;/span&gt; for line = (read-line stream nil nil)&lt;br /&gt;                        while line&lt;br /&gt;                        do (&lt;span class="keyword"&gt;when&lt;/span&gt; (scan regexp line)&lt;br /&gt;                             (format t &lt;span class="string"&gt;"~A~%"&lt;/span&gt; line)))))&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(grep "Ubuntu" "/etc/motd")&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;The programs included with the Ubuntu system are free software;&lt;br /&gt;Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by&lt;/div&gt;&lt;/pre&gt;At this point you've seen what a typical CL scripting session is like, working from the bottom up.&lt;br /&gt;&lt;br /&gt;Now the above was a pretty simplistic example, and it used (apart from CL-PPCRE) good old fashioned CL, which hasn't changed since the mid-1980s.  Since then, scripting languages like Perl have been invented by smart people like Larry Wall, having many built-in features that are missing from stock CL.  Why suffer without them?  It would seem that you have to either choose between a general-purpose, compiled language like CL or purpose-built, interpreted Perl.  Fortunately, this is &lt;i&gt;not&lt;/i&gt; the case!  I've found that each piece of Perl functionality I've wanted has taken on the order of 45 minutes to 1 hour to implement using CL's powerful &lt;a href="http://www.lisp.org/HyperSpec/Body/mac_defmacro.html"&gt;DEFMACRO&lt;/a&gt; facility.  &lt;br /&gt;&lt;br /&gt;Let's take, for example, Perl's magic &amp;lt;&amp;gt; operator.  In Perl, you can write:&lt;pre class="code"&gt;#!/usr/bin/env perl&lt;br /&gt;# file: "foo.pl"&lt;br /&gt;&lt;br /&gt;for $line (&amp;lt;&amp;gt;) {&lt;br /&gt;    print $line;&lt;br /&gt;}&lt;/pre&gt;Which will slurp up all the filenames on the command-line and let you iterate over each line.  E.g.,&lt;pre class="code"&gt;% foo.pl /etc/motd /etc/resolv.conf&lt;/pre&gt;would print out those files, sort of like &lt;a href="http://en.wikipedia.org/wiki/Cat_(Unix)"&gt;cat&lt;/a&gt;.  Wouldn't it be nice to have a similar feature in CL?  Let's start by taking our &lt;b&gt;GREP&lt;/b&gt; function and adding the ability to customize the action taken on each line:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;do-lines&lt;/span&gt; (fn filename)&lt;br /&gt;  (&lt;span class="keyword"&gt;with-open-file&lt;/span&gt; (stream filename)&lt;br /&gt;    (&lt;span class="keyword"&gt;loop&lt;/span&gt; for line = (read-line stream nil nil)&lt;br /&gt;       while line&lt;br /&gt;       do (&lt;span class="keyword"&gt;when&lt;/span&gt; (scan regexp line)&lt;br /&gt;            (funcall fn line)))))&lt;/pre&gt;&lt;B&gt;DO-LINES&lt;/B&gt; is almost exactly the same as &lt;B&gt;GREP&lt;/B&gt;, except that it just calls a function on each line of the file rather than assume grep-like behavior.  To verify that we can implement &lt;span style="font-weight:bold;"&gt;GREP&lt;/span&gt; in terms of &lt;span style="font-weight:bold;"&gt;DO-LINES&lt;/span&gt; we try the following at the command-line:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(do-lines (lambda (line)&lt;br /&gt;                 (when (scan "Ubuntu" line) (format t "~A~%" line))) "/etc/motd")&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;The programs included with the Ubuntu system are free software;&lt;br /&gt;Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by&lt;/div&gt;&lt;/pre&gt;Isn't that great?  Baaaarf.  Not really, eh?  Kind of verbose compared to the Perl code, and it doesn't even handle multiple files like the Perl code does!  &lt;br /&gt;&lt;br /&gt;What we'd &lt;i&gt;really&lt;/i&gt; like to be able to do is something CL-esque yet approximately equal to the Perl code in brevity.  This sounds like a good use for a CL macro.  Let's start by just fantasizing about what we'd like:&lt;pre class="code"&gt;(with-lines-from-files (line &lt;span class="string"&gt;"/etc/motd"&lt;/span&gt; &lt;span class="string"&gt;"/etc/resolv.conf"&lt;/span&gt;) &lt;br /&gt;  (format t &lt;span class="string"&gt;"~A~%"&lt;/span&gt; line)) &lt;span class="comment-delimiter"&gt;; &lt;/span&gt;&lt;span class="comment"&gt;just a simple example, could put anything here&lt;/span&gt;&lt;/pre&gt;Well,  before going further, we need to enhance &lt;span style="font-weight:bold;"&gt;DO-LINES&lt;/span&gt; to take more than one file:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;do-all-lines&lt;/span&gt; (fn &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; filenames)&lt;br /&gt;  (&lt;span class="keyword"&gt;dolist&lt;/span&gt; (filename-or-list filenames)&lt;br /&gt;    &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;For convenience we allow you also to pass in lists of filenames&lt;br /&gt;&lt;/span&gt;    (&lt;span class="keyword"&gt;dolist&lt;/span&gt; (filename (&lt;span class="keyword"&gt;typecase&lt;/span&gt; filename-or-list&lt;br /&gt;                        (cons filename-or-list)&lt;br /&gt;                        (t (list filename-or-list))))&lt;br /&gt;      (&lt;span class="keyword"&gt;with-open-file&lt;/span&gt; (stream filename)&lt;br /&gt;        (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;           for line = (read-line stream nil nil)&lt;br /&gt;           while line&lt;br /&gt;           do (funcall fn line))))))&lt;/pre&gt;Now we just need to write a macro so that you don't have to write all that crazy &lt;span style="font-weight:bold;"&gt;LAMBDA&lt;/span&gt; stuff:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;with-lines-from-files&lt;/span&gt; ((var &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; filenames) &lt;span class="type"&gt;&amp;amp;body&lt;/span&gt; body)&lt;br /&gt;  `(do-all-lines (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (,var) ,@body) ,@filenames))&lt;/pre&gt;Armed with this macro, we can re-write &lt;span style="font-weight:bold;"&gt;GREP&lt;/span&gt; with way fewer lines (and the ability to grep across multiple files):&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;grep&lt;/span&gt; (regexp &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; filenames)&lt;br /&gt;  (with-lines-from-files (line filenames)&lt;br /&gt;    (&lt;span class="keyword"&gt;when&lt;/span&gt; (scan regexp line)&lt;br /&gt;      (format t &lt;span class="string"&gt;"~A~%"&lt;/span&gt; line))))&lt;br /&gt;&lt;span class="slime-repl-prompt"&gt;CL-USER&amp;gt;&lt;/span&gt; &lt;span class="slime-repl-input"&gt;(grep "Ubuntu|name" "/etc/motd" "/etc/resolv.conf")&lt;/span&gt;&lt;br /&gt;&lt;div class="codepop"&gt;The programs included with the Ubuntu system are free software;&lt;br /&gt;Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by&lt;br /&gt;nameserver 68.87.69.146&lt;br /&gt;nameserver 68.87.85.98&lt;/div&gt;&lt;/pre&gt;If it weren't for macros, we'd be stuck implementing grep with the clunky use of &lt;span style="font-weight:bold;"&gt;LAMBDA&lt;/span&gt;:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;clunky-grep&lt;/span&gt; (regexp &lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; filenames)&lt;br /&gt;  (do-all-lines (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (line) &lt;br /&gt;                  (&lt;span class="keyword"&gt;when&lt;/span&gt; (scan regexp line) &lt;br /&gt;                    (format t &lt;span class="string"&gt;"~A~%"&lt;/span&gt; line)))&lt;br /&gt;    filenames))&lt;/pre&gt;The &lt;span style="font-weight:bold;"&gt;LAMBDA&lt;/span&gt;-free version of &lt;span style="font-weight:bold;"&gt;GREP&lt;/span&gt; is &lt;i&gt;much&lt;/i&gt; easier to read, it almost reads like English: "with lines from the files, when lines scan the regexp, format the line".  Whereas the &lt;span style="font-weight:bold;"&gt;LAMBDA&lt;/span&gt; version reads "with lines, LAMBDA (huh!!!?), when lines scan the regexp, format the line, across the filenames".  No contest!&lt;br /&gt;&lt;br /&gt;Similarly, other Perl operations can be easily implemented with macros:&lt;pre class="code"&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;The following macro is mostly like Perl's foreach, in the sense&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;that you can pass in as many references to sequences or "scalars"&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;as you want and it will iterate over them and allow you to modify&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;them.  Unlike the Perl code, it sets the variable IT to each&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;element rather than $_.  Also, you have to just pass in the hash&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;table directly, not a flattened list of hash keys.&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;perl-foreach&lt;/span&gt; ((&lt;span class="type"&gt;&amp;amp;rest&lt;/span&gt; refs) &lt;span class="type"&gt;&amp;amp;body&lt;/span&gt; body)&lt;br /&gt;  (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((gensyms (&lt;span class="keyword"&gt;loop&lt;/span&gt; repeat (length refs) collect (gensym))))&lt;br /&gt;    (list*&lt;br /&gt;     'let&lt;br /&gt;     (mapcar #'list gensyms refs)&lt;br /&gt;     (&lt;span class="keyword"&gt;loop&lt;/span&gt;&lt;br /&gt;        for ref in refs&lt;br /&gt;        and indirect-ref in gensyms&lt;br /&gt;        collect&lt;br /&gt;        `(&lt;span class="keyword"&gt;typecase&lt;/span&gt; ,indirect-ref&lt;br /&gt;           (hash-table &lt;br /&gt;            (maphash #'(&lt;span class="keyword"&gt;lambda&lt;/span&gt; (key value)&lt;br /&gt;                         (&lt;span class="keyword"&gt;declare&lt;/span&gt; (ignore value))&lt;br /&gt;                         (&lt;span class="keyword"&gt;symbol-macrolet&lt;/span&gt; ((it (gethash key ,indirect-ref)))&lt;br /&gt;                           ,@body))&lt;br /&gt;                     ,indirect-ref))&lt;br /&gt;           ((and (or vector list) (not string))&lt;br /&gt;            (map-into ,indirect-ref&lt;br /&gt;                      #'(&lt;span class="keyword"&gt;lambda&lt;/span&gt; (it)&lt;br /&gt;                          ,@body&lt;br /&gt;                          it)&lt;br /&gt;                      ,indirect-ref))&lt;br /&gt;           (t &lt;br /&gt;            (&lt;span class="keyword"&gt;symbol-macrolet&lt;/span&gt; ((it ,ref))&lt;br /&gt;              ,@body)))))))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;trim whitespace in the scalar, the list, the array, and all the&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;values in the hash&lt;br /&gt;&lt;/span&gt;(perl-foreach (scalar my-list my-array my-hash)&lt;br /&gt;  (setf it (regex-replace &lt;span class="string"&gt;"^\\s+"&lt;/span&gt; it &lt;span class="string"&gt;""&lt;/span&gt;))&lt;br /&gt;  (setf it (regex-replace &lt;span class="string"&gt;"\\s+$"&lt;/span&gt; it &lt;span class="string"&gt;""&lt;/span&gt;)))&lt;/pre&gt;Or:&lt;pre class="code"&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;perl-splice&lt;/span&gt; (sequence-place &lt;span class="type"&gt;&amp;amp;optional&lt;/span&gt; (offset 0) length replacement-sequence)&lt;br /&gt;  (&lt;span class="keyword"&gt;let*&lt;/span&gt; ((seq (gensym &lt;span class="string"&gt;"SEQUENCE-PLACE-"&lt;/span&gt;))&lt;br /&gt;         (off-arg (gensym &lt;span class="string"&gt;"OFFSET-ARG-"&lt;/span&gt;))&lt;br /&gt;         (off (gensym &lt;span class="string"&gt;"OFFSET-"&lt;/span&gt;))&lt;br /&gt;         (len (gensym &lt;span class="string"&gt;"LENGTH-"&lt;/span&gt;))&lt;br /&gt;         (end (gensym &lt;span class="string"&gt;"END-"&lt;/span&gt;))&lt;br /&gt;         (rep (gensym &lt;span class="string"&gt;"REPLACEMENT-SEQUENCE-"&lt;/span&gt;))&lt;br /&gt;         (left-part (list `(subseq ,seq 0 ,off)))&lt;br /&gt;         (right-part (&lt;span class="keyword"&gt;when&lt;/span&gt; length&lt;br /&gt;                       (list `(subseq ,seq ,end)))))&lt;br /&gt;    `(&lt;span class="keyword"&gt;let*&lt;/span&gt; ((,seq ,sequence-place)&lt;br /&gt;            (,off-arg ,offset)&lt;br /&gt;            (,off (&lt;span class="keyword"&gt;if&lt;/span&gt; (minusp ,off-arg)&lt;br /&gt;                      (+ (length ,seq) ,off-arg)&lt;br /&gt;                      ,off-arg))&lt;br /&gt;            (,len ,length)&lt;br /&gt;            (,end (&lt;span class="keyword"&gt;when&lt;/span&gt; ,len&lt;br /&gt;                    (&lt;span class="keyword"&gt;if&lt;/span&gt; (minusp ,len)&lt;br /&gt;                        (+ (length ,seq) ,len)&lt;br /&gt;                        (+ ,off ,len))))&lt;br /&gt;            (,rep ,replacement-sequence))&lt;br /&gt;       (&lt;span class="keyword"&gt;prog1&lt;/span&gt; (subseq ,seq ,off ,end)&lt;br /&gt;         (&lt;span class="keyword"&gt;when&lt;/span&gt; (or ,rep (not (eql ,off ,end)))&lt;br /&gt;           (setf ,sequence-place (concatenate (&lt;span class="keyword"&gt;typecase&lt;/span&gt; ,seq&lt;br /&gt;                                                (cons 'list)&lt;br /&gt;                                              (t 'vector))&lt;br /&gt;                                            ,@left-part&lt;br /&gt;                                            ,rep&lt;br /&gt;                                            ,@right-part)))))))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;Now the syntax is almost exactly the same.&lt;br /&gt;&lt;/span&gt;(setf front (perl-splice my-array 0 n))&lt;br /&gt;(setf end (perl-splice my-array 0 (- n)))&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/B6TyJ0zEI20" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/7915719393787254012/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=7915719393787254012" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/7915719393787254012?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/7915719393787254012?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/B6TyJ0zEI20/scripting-in-cl.html" title="Scripting in CL" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>3</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2008/02/scripting-in-cl.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0MARnwyeyp7ImA9WxZVEUk.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-5059573551398249900</id><published>2007-11-06T21:23:00.000-08:00</published><updated>2008-03-21T16:17:27.293-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-21T16:17:27.293-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="emacs" /><title>Effective .emacs</title><content type="html">&lt;span style="font-weight: bold;"&gt;Tip #0: Use Emacs 22&lt;/span&gt;&lt;br /&gt;Emacs 22 is super stable.  About half of my .emacs file (before I cleaned it up) was loading stuff that's now part of Emacs 22 and has autoloads.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #1: Never quit Emacs&lt;/span&gt;&lt;br /&gt;Okay, this has nothing to do with your .emacs file, but I have to put it in here.  Just because your .emacs file should load quickly doesn't imply that you should quit and restart all the time.  Figure it out!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #2: (require 'cl)&lt;/span&gt;&lt;br /&gt;I put this at the top of my .emacs.  It's a no-brainer.  It adds in a ton of compatibility with &lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;CL&lt;/a&gt;, so that you can just use the CL functions you know and love (well, most of them, anyway), without a second thought.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #3: Never LOAD, never REQUIRE&lt;/span&gt;&lt;br /&gt;Your .emacs file shouldn't contain any calls to &lt;span style="font-weight: bold;"&gt;LOAD &lt;/span&gt;or &lt;span style="font-weight: bold;"&gt;REQUIRE &lt;/span&gt;(which are slow and often cause errors on startup).  The only possible exceptions are loading files that contain nothing but autoloads (or similar stuff).  How do you avoid loads and requires?  First try removing each call to &lt;span style="font-weight: bold;"&gt;LOAD &lt;/span&gt;or &lt;span style="font-weight: bold;"&gt;REQUIRE &lt;/span&gt;&lt;span&gt;to &lt;/span&gt;see if it's needed at all.  Often (e.g., if you follow &lt;span style="font-weight: bold;"&gt;Tip #0&lt;/span&gt;) Emacs already has autoloads in place for the library already (e.g., "cc-mode").  For other libraries, where that's not true, put your own autoloads in your .emacs file.  For example, rather than load &lt;a href="http://common-lisp.net/project/slime/"&gt;SLIME&lt;/a&gt; in my .emacs (so I can bind the &lt;span style="font-family:courier new;"&gt;F1 &lt;/span&gt;key to &lt;span style="font-weight: bold;"&gt;SLIME-SELECTOR&lt;/span&gt;), instead I have:&lt;pre class="code"&gt;(autoload 'slime-selector "slime" t)&lt;/pre&gt;The only call to &lt;span style="font-weight: bold;"&gt;LOAD &lt;/span&gt;in my .emacs file is for "erlang-start", but if you look inside the file you can see it contains only autoloads (and morally equivalent stuff).  I also load the custom file, but that's different, see &lt;span style="font-weight: bold;"&gt;Tip #7&lt;/span&gt;.  I don't have a single call to &lt;span style="font-weight: bold;"&gt;REQUIRE&lt;/span&gt; (beyond that mandated by &lt;span style="font-weight: bold;"&gt;Tip #2&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #4: Understand and use EVAL-AFTER-LOAD&lt;/span&gt;&lt;br /&gt;Another reason why you might have strewn needless &lt;span style="font-weight: bold;"&gt;REQUIRE&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;LOAD&lt;/span&gt; calls throughout your .emacs file is that you need to call a function from a specific library.  For example, let's say you want to set your default SQL database type to MySQL:&lt;pre class="code"&gt;(sql-set-product 'mysql)&lt;/pre&gt;If you put this in your .emacs, you'll get an error because the SQL library isn't loaded so &lt;span style="font-weight: bold;"&gt;SQL-SET-PRODUCT&lt;/span&gt; isn't yet defined.  But before you add a &lt;span style="font-weight: bold;"&gt;LOAD&lt;/span&gt; or &lt;span style="font-weight: bold;"&gt;REQUIRE&lt;/span&gt;, stop!  Instead do:&lt;pre class="code"&gt;(eval-after-load "sql"&lt;br /&gt;  '(progn&lt;br /&gt;     (sql-set-product 'mysql)&lt;br /&gt;     ;; any other config specific to sql&lt;br /&gt;  ))&lt;/pre&gt;As the name suggests, this will defer calling that code until the SQL module is actually loaded.  This saves startup time &lt;i&gt;and&lt;/i&gt; prevents errors!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #5: Time your .emacs&lt;/span&gt;&lt;br /&gt;You really ought to know how much time it's taking to load your .emacs file.  Use the following in your .emacs:&lt;pre class="code"&gt;(require 'cl) ; a rare necessary use of REQUIRE&lt;br /&gt;(defvar *emacs-load-start* (current-time))&lt;br /&gt;&lt;br /&gt;;; rest of your .emacs goes here&lt;br /&gt;&lt;br /&gt;(message "My .emacs loaded in %ds" (destructuring-bind (hi lo ms) (current-time)&lt;br /&gt;                           (- (+ hi lo) (+ (first *emacs-load-start*) (second *emacs-load-start*)))))&lt;/pre&gt;After Emacs finishing initializing, you can switch to the &lt;span style="font-family:courier new;"&gt;*Messages*&lt;/span&gt; buffer and see how much of that time was taken by loading your .emacs.  Mine now contributes less than one second!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #6: Set background colors&lt;/span&gt;&lt;br /&gt;Don't just stand for the default colors!  Set them to what you really want.  In my case I want a reverse video effect:&lt;pre class="code"&gt;(set-background-color "black")&lt;br /&gt;(set-face-background 'default "black")&lt;br /&gt;(set-face-background 'region "black")&lt;br /&gt;(set-face-foreground 'default "white")&lt;br /&gt;(set-face-foreground 'region "gray60")&lt;br /&gt;(set-foreground-color "white")&lt;br /&gt;(set-cursor-color "red")&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Tip #7: Separate custom file&lt;/span&gt;&lt;br /&gt;It's annoying to have your .emacs file modified by Emacs' "custom" library, especially if you check in your .emacs to a source code control system such as Subversion (which you should do) and synchronize it on multiple machines.  Keep those customizations in a separate file:&lt;pre class="code"&gt;(setq custom-file "~/.emacs-custom.el")&lt;br /&gt;(load custom-file 'noerror)&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/q06cscZnAuM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/5059573551398249900/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=5059573551398249900" title="15 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/5059573551398249900?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/5059573551398249900?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/q06cscZnAuM/effective-emacs.html" title="Effective .emacs" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>15</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/11/effective-emacs.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEMRn05fSp7ImA9WxZVEUk.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-8653411735017600206</id><published>2007-09-02T10:41:00.000-07:00</published><updated>2008-03-21T16:38:07.325-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-21T16:38:07.325-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>The Icarus Ultimatum</title><content type="html">It is unlikely that &lt;i&gt;every&lt;/i&gt; programmer is familiar with &lt;a href="http://en.wikipedia.org/wiki/Icarus"&gt;Icarus&lt;/a&gt;, but I bet that almost all programmers have something in common with him.  Programmers are saturated with advice &lt;i&gt;not&lt;/i&gt; to do things, similar to the advice Icarus' dad gave him about aviation.  Don't use threads unless you really know what you're doing (and then don't use them anyway.)  Don't use new language features (they're too dangerous.)  Use the "right tool for the right job" (i.e., not the one &lt;i&gt;you&lt;/i&gt; like.)  Don't "prematurely" optimize (note: it's &lt;i&gt;always&lt;/i&gt; premature.)  Don't use macros, nobody will be able to read your code.  Don't use multiple inheritance.  You ain't gonna need it (you dork!)  Don't, don't, don't; no, No, NO!  For the rest of this essay I will refer to such advice as "Dead" advice, in honor of Icarus' father, Daedalus (and also to honor my own lack of spelling ability.)&lt;br /&gt;&lt;br /&gt;Let's remember one thing about Icarus.  Yes, he died a fiery, horrible death.  But at the same time, he flew close to the freakin' &lt;i&gt;Sun&lt;/i&gt;!  How cool is that?  Likewise, the most exciting developments in software have been &lt;i&gt;crazy stupid&lt;/i&gt;.  I remember how quixotic the notion that you could index and search the entire Internet (or even a significant part of it) once seemed.  Only by thinking big does anything cool get done, it doesn't get done by sitting around pondering all the things you &lt;i&gt;shouldn't&lt;/i&gt; be doing.&lt;br /&gt;&lt;br /&gt;Dead advice exists for many reasons, of course.  Programming is so open-ended that it is easy to create unmaintainable, unreliable, slow software.  Programs start out as a blank slate; one can do &lt;i&gt;anything&lt;/i&gt;.  Without discipline, &lt;i&gt;anything&lt;/i&gt; turns into &lt;i&gt;unmitigated crap&lt;/i&gt;.  Thus experienced programmers observe mistakes (including their own) and, sometimes, craft memorable axioms as a result.  Unfortunately, these well-intentioned rules-of-thumb often turn into blindly-followed commandments by the time they penetrate the consciousness of millions of programmers.&lt;br /&gt;For example, the original advice about &lt;a href="http://a-nickels-worth.blogspot.com/2007/08/proactive-optimization.html"&gt;premature optimization&lt;/a&gt; has become twisted over the years, such that programmers are afraid ever to optimize lest their effort be labeled "premature".  Similarly, Microsoft's infamous detour into Hungarian notation started with a &lt;a href="http://www.joelonsoftware.com/articles/Wrong.html"&gt;misunderstood&lt;/a&gt; paper by future astronaut Charles Simonyi.  But this sort of telephone game isn't the only source of Dead advice.&lt;blockquote&gt;"It's not guns that kill people, It's these little hard things!"&lt;br /&gt;   --- &lt;i&gt;the Flash from "The Trickster Returns"&lt;/i&gt;&lt;/blockquote&gt;Sometimes Dead advice emerges not from a misinterpretation of the original advice but from a failure to perceive a problem's underlying cause.  The treatment of threads in computer science illustrates this phenomenon well.  Typical computer science programs teach little, if anything about threads; threads are an "implementation detail" with minor theoretical import (compared, say, to data structures, Big O, recursion, etc).  What one &lt;i&gt;does&lt;/i&gt; learn about them, though, makes them sound cool.  Heck, let's face it, they &lt;i&gt;are&lt;/i&gt; pretty darn cool.  They're the expression of multitasking; a piece of magic.  Junior programmers are disappointed when, on their first job, their mentors seem ready to pour boiling sulphuric acid on their keyboard if they so much as &lt;i&gt;mention&lt;/i&gt; the possibility of using a thread.  "My advice is not to use threads unless you're extremely experienced, a thread wizard, and even then don't use them."  That's what they'll hear.&lt;br /&gt;&lt;br /&gt;I'm going to step out on a limb here, insert earplugs to drown out the chorus of "boos" that I can already hear even though nobody's read this post yet, and state: there's nothing wrong with using threads.  This is a big reversal for me; I spread the &lt;i&gt;&lt;a href="http://home.pacbell.net/ouster/threads.pdf"&gt;threads are evil&lt;/a&gt;&lt;/i&gt; gospel for many years.  Yet I have confidence in this assertion for a number of reasons, foremost among them being that the admonitions &lt;i&gt;against&lt;/i&gt; threads haven't helped much.  How many applications have you used where the user interface freezes up every time the application does something that takes longer than a second or two?  How many times have you found Cancel buttons (you know, those ones you're trying to hit when you realize you're accidentally deleting all of your un-backed-up files?) that take minutes to have any effect?&lt;br /&gt;&lt;br /&gt;If anti-thread prohibitions haven't worked, what would?  I've been casually researching this issue for a long time.  At first I decided, like many, that threads are indeed evil, and that &lt;a href="http://en.wikipedia.org/wiki/Event-driven_programming"&gt;event-driven programming&lt;/a&gt; was the &lt;i&gt;only&lt;/i&gt; way to go (programmers love nothing more than a dichotomy.)  When other programmers would point out to me that event-driven programming is, at times, a giant pain in the rear, I'd silently mark them as wimps.  Finally, I read a series of papers, including &lt;a href="http://www.usenix.org/events/usenix02/full_papers/adyahowell/adyahowell.pdf"&gt;this one&lt;/a&gt;, that convinced me there was, at least, more to the story.  Even more recently, someone clued me in to the &lt;a href="http://erlang.org/"&gt;work&lt;/a&gt; of &lt;a href="http://www.sics.se/~joe/ericsson/"&gt;Joe Armstrong&lt;/a&gt;, one of the only people outside of academia to have stepped back from the problem of threads and tackle, instead, &lt;i&gt;concurrency&lt;/i&gt;, which was the &lt;i&gt;real&lt;/i&gt; issue all along.  By far the coolest thing Joe did was realize that you can in fact fly close to the Sun and spawn &lt;i&gt;as many darn "threads" as the problem you're solving truly warrants&lt;/i&gt;.  You can also use events, too, there having been, it turns out, no dichotomy between events and threads either.&lt;br /&gt;&lt;br /&gt;I found this revelatory after having wasted a lot of time, over the years, writing things like HTTP parsers in frameworks that either force you to use only events, or frameworks that let you spawn threads easily (though not necessarily efficiently) but have no standard event-passing mechanism.  It's not just that I didn't think of it myself, it's that almost everyone I talked to about this sort of issue was either in the "threads are evil" camp or the "events are confusing" camp.  I wasted a lot of time following bad advice.&lt;br /&gt;&lt;br /&gt;Experiences with Dead advice such as "threads are evil" have led me to question other Dead advice, such as:&lt;ul&gt;&lt;li&gt;Premature optimization is the root of all evil.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Never use multiple inheritance.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;YAGNI.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Never use macros.&lt;/li&gt;&lt;/ul&gt;Each of these have a kernel of truth to them, but are also too easily misunderstood and have not had the &lt;i&gt;overall&lt;/i&gt; effect on programming that was originally intended.  If I have some time I'll try to give more examples of each of the above.  In the meantime, I do have to mention one piece of (arguably) Dead advice that I haven't found any fault with yet:&lt;ul&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;Don't repeat yourself&lt;/a&gt;.&lt;br /&gt;&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/O92yCdyA0gQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/8653411735017600206/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=8653411735017600206" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/8653411735017600206?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/8653411735017600206?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/O92yCdyA0gQ/icarus-ultimatum.html" title="The Icarus Ultimatum" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>2</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/09/icarus-ultimatum.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIDQXg7fyp7ImA9WB5bEUw.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-3233242591731357229</id><published>2007-08-25T23:36:00.000-07:00</published><updated>2007-08-26T00:32:50.607-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-08-26T00:32:50.607-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><title>Macrophobia</title><content type="html">As a fledgling CL programmer, I've found macros to be one of its most enduring charms.  Yet it is also one of its most criticized (or at least feared) features.  Much like threads have become the bogeyman of the programming world, so macros would be too, if more than a handful of programmers actually knew about them.  Among this handful, though, it would be nice to dispel the myth that macros are too powerful.&lt;br /&gt;&lt;br /&gt;I mean, obviously, they are, in some sense, too powerful.  The common complaint about them is that junior programmers (especially) can create what appear to be language-level constructs that confuse the heck out of everyone else, impairing the code's "readability".  As with most broadly-accepted maxims (in this case, broadly accepted by the 15 people in the world who actually know about the issue in the first place), it is both well-intended, intuitively reasonable, and incorrect, for the simple reason that if, at your company, junior programmers are running amok with no mentors to teach them, the code will be unreadable whether or not it uses macros.&lt;br /&gt;&lt;br /&gt;Frankly, I'd like to dispel the whole myth that there's great import to be placed on the "readability" of a programming language.  I see blog after blog, each about as scientifically sound as my 10th grade Chemistry report (in case you're wondering, I'm not a chemist), about whether Perl is more readable than C, or vice-versa, or whether Python is more readable than CL, etc.  This tiresome, endless debate stirs the fanboy  in all of us, but is missing not just the forest for the trees, but the solar system for the planet.&lt;br /&gt;&lt;br /&gt;I've read a lot of code in my day, I'll have you know.  What matters most to readability is, first, comments, followed by identifier names, followed by there being as little code as possible, followed by program structure.  Ok, I can hear the chorus of "boos" in the background at the preceding statement.  All I can say is, most programmers have no idea how to write good comments (even though, as they say, the "force" is with them; they just have no idea, and lack a Yoda-like figure to explain it to them).  When comments are good, there is nothing quite like it.  The code could be written in &lt;a href="http://en.wikipedia.org/wiki/Text_Editor_and_Corrector"&gt;TECO&lt;/a&gt; for all I care.  Maybe blog, will I, about it, some day.&lt;br /&gt;&lt;br /&gt;So macros, in fact, cannot really get in the way of readability.  In fact, they have the potential to greatly enhance readability, used judiciously (and as pointed out earlier, if your company is not run judiciously, you have larger issues to work out first).  How many of you out there have worked somewhere that had some kind of coding guidelines?  I'd warrant that a fair number of you have.  I certainly have.  I'm talking about stuff like &lt;a href="http://www.doc.ic.ac.uk/lab/cplus/c++.rules/"&gt;Ellemtel's Programming in C++, Rules and Recommendations&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Give those rules a once-over, if you haven't already.  It's pretty complicated stuff.  Use this feature this way, use that feature that other way, don't do this, do do that, etc, etc.  Yawn.  I've worked at companies that successfully enforced such rules (at least 80% of the time), and some that failed utterly.  In both cases, it was wickedly hard to enforce these rules (successfully or not).  All programmers had to read the coding guidelines document, understand it, remember it, and periodically re-read it when it was updated.  Senior programmers had to enforce the guidelines during code reviews (which was sometimes easier than other times, depending on the stubbornness of the reviewee).&lt;br /&gt;&lt;br /&gt;At more sophisticated shops, one could imagine, at least, that some of the rules would be enforced by build scripts.  A poor solution if ever there was one.  For one thing, build scripts tend to be written in a different language (e.g., Perl), and usually resort to crude regexp-matching in order to catch offending code.  Only a handful of programmers at your company are brave (or foolish) enough to grok the build scripts.  These poor souls find themselves, before too long, at the bottom of a pit of despair from which they never return (save by leaving for another team or company).  Eventually they label themselves with self-deprecating monikers such as "build bitch".&lt;br /&gt;&lt;br /&gt;Macros afford the possibility, at least, that some of these conventions be enforced within the language itself, rather than by a bunch of hacky Perl scripts (or no automated process at all).  Take, for instance, the use of CL's &lt;a href="http://www.lisp.org/HyperSpec/Body/mac_defclass.html"&gt;DEFCLASS&lt;/a&gt;.  Now DEFCLASS has roughly one grillion options for how to do this, that, or the other thing.  It's also remarkably lax about, well, almost everything.  If you want a member variable ("slot") called FOO but wish for the accessor to it to be called BAR, you can do that.  &lt;br /&gt;&lt;br /&gt;If you wanted to prevent such wanton creativity on the part of your less-trustworthy programmers, you could do so by writing a macro wrapping DEFCLASS which might both limit its complexity and enforce constraints such as the regular naming of accessors and the like.  You could prevent the use of multiple inheritance if you found it too frightening (I'm not suggesting you go out and do this, just pointing out that you could).  These rules would be enforce using code written in the same language as everything else, making them easier to write (in that they can harness the reflective power of the language itself, i.e., no hack regexps) and easier to find volunteers to maintain and develop.&lt;br /&gt;&lt;br /&gt;I could go on and on about this, but it's getting late so I'll just wrap it up there.  One last thing, though, I urge the readers of this blog (all three of you) to re-evaluate the various maxims you may have assimilated over the years.  Are threads really that bad if there is a problem facing you that is genuinely concurrent?  Are you really &lt;a href="http://a-nickels-worth.blogspot.com/2007/08/proactive-optimization.html"&gt;optimizing prematurely&lt;/a&gt;?  Is there really such thing as a "scripting language"?  Should operating systems and programming languages keep each other at arm's length?  The list goes on.&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/0HSzo-cd3nQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/3233242591731357229/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=3233242591731357229" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/3233242591731357229?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/3233242591731357229?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/0HSzo-cd3nQ/macrophobia.html" title="Macrophobia" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>8</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/08/macrophobia.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08FQHo_fyp7ImA9WB5bEE8.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-5696334730438212405</id><published>2007-08-24T23:58:00.000-07:00</published><updated>2007-08-25T00:10:11.447-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-08-25T00:10:11.447-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="programming" /><title>Proactive Optimization</title><content type="html">In the programming world, one of the most widely repeated maxims is, "Premature optimization is the root of all evil."  Having found this to be one of the most well-intended yet misunderstood pieces of advice in the history of the Universe, I was all set to write a blog about why.  Unfortunately, for me, a quick trip to Wikipedia led me to this article: &lt;a href="http://www.acm.org/ubiquity/views/v7i24_fallacy.html"&gt;The Fallacy of Premature Optimization&lt;/a&gt;, by Randall Hyde, which said everything I intended to say, and more, and did so more eloquently that I could ever hoped to have (although I might have put in some better jokes).&lt;br /&gt;&lt;br /&gt;So the only thing I really have to add to that article is the following crude attempt at marketing: let's put a little spin on that phrase and turn it around!  Whenever you hear "premature optimization is the root of all evil", retort (politely) with "but proactive optimization is the root of all goodness."&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/NJScnUQhIgQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/5696334730438212405/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=5696334730438212405" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/5696334730438212405?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/5696334730438212405?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/NJScnUQhIgQ/proactive-optimization.html" title="Proactive Optimization" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>0</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/08/proactive-optimization.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AAR30zeSp7ImA9WxZVEUk.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-7897615734888927251</id><published>2007-06-17T13:16:00.000-07:00</published><updated>2008-03-21T16:22:26.381-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-21T16:22:26.381-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rogue" /><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><category scheme="http://www.blogger.com/atom/ns#" term="c" /><title>CL-ROGUE</title><content type="html">Over the last six months I have been porting the &lt;a href="http://roguelikedevelopment.org/archive/files/executables/rogue3.6.2-linux.tar.gz"&gt;1981 version&lt;/a&gt; of &lt;a href="http://www.wichman.org/roguehistory.html"&gt;Rogue&lt;/a&gt; to &lt;a href="http://en.wikipedia.org/wiki/Common_Lisp"&gt;CL&lt;/a&gt;.  My intention was to learn the basics of CL;  it's a straight port of the game.  I wish I had thought of porting something sooner (especially Rogue, which I once ported to &lt;a href="http://www.geocities.com/originalravinray/geos/history.htm"&gt;PC/GEOS&lt;/a&gt;), but for some reason this didn't occur to me until recently.  It is a great way to learn a language if you don't have much time.  I heartily recommend it (over, say, trying to think of a "pet project" to code up in the new language, which is cool, but requires a substantial investment).  With porting, the project is already written and working, and you can pick it up and drop it frequently (if you have only a few hours a week, for example) without losing too much context (the original authors did all the hard work!)  In the case of C and CL, porting was a good way to learn all the side-effect-y features of CL that are often glossed over in CL books (which focus more on functional programming).&lt;br /&gt;&lt;br /&gt;Having spent most of my career doing C/C++/Unix (and a bit of C#/Windows), it is interesting to see first-hand the alternative road that CL would have taken us down.  One cannot compare C and CL directly, because in CL the operating system and language are indivisible.  That is probably its biggest strength.  By contrast, although C is the "first class" language of Unix, it was not treated especially royally.  For example, you cannot use C directly in the command shell.  When I was first learning Unix, it was confusing that I couldn't call C functions from the so-called "C Shell".&lt;br /&gt;&lt;br /&gt;At first, CL's REPL seems alien.  Once you realize it is the same thing as the command shell, all makes sense.  For example, I had a little difficulty understanding what &lt;a href="http://www.cliki.net/asdf"&gt;ASDF&lt;/a&gt; was until I realized it's equivalent to &lt;b&gt;make&lt;/b&gt; or &lt;b&gt;ant&lt;/b&gt;.  Many CL functions and packages correspond to Unix utilities that you'd normally have to run from the command-line.  Likewise, the lack of an Eclipse- or Visual Studio-like debugger at first seemed to be a major weakness, until it dawned on me that &lt;i&gt;you're always in the debugger&lt;/i&gt; in CL.&lt;br /&gt;&lt;br /&gt;Porting C to CL was mostly mechanical and surprisingly easy.  What was most intriguing was that some relatively obscure-sounding features of CL proved extremely useful (unfortunately I didn't discover this until late in the game).  In particular, &lt;a href="http://www.lisp.org/HyperSpec/Body/speope_symbol-macrolet.html"&gt;SYMBOL-MACROLET&lt;/a&gt; and &lt;a href="http://www.lisp.org/HyperSpec/Body/mac_define-symbol-macro.html"&gt;DEFINE-SYMBOL-MACRO&lt;/a&gt; helped immensely (or at least they did once I figured out how to use them!)&lt;br /&gt;&lt;br /&gt;The port itself is &lt;a href="http://code.google.com/p/cl-rogue/"&gt;here&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/YmG2vJvQGDw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/7897615734888927251/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=7897615734888927251" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/7897615734888927251?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/7897615734888927251?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/YmG2vJvQGDw/cl-rogue.html" title="CL-ROGUE" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>0</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/06/cl-rogue.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8CRX88cCp7ImA9WxZVEUk.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-503788244167924508</id><published>2007-03-12T23:26:00.000-07:00</published><updated>2008-03-21T16:07:44.178-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-21T16:07:44.178-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="star wars" /><category scheme="http://www.blogger.com/atom/ns#" term="java" /><category scheme="http://www.blogger.com/atom/ns#" term="smalltalk" /><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><category scheme="http://www.blogger.com/atom/ns#" term="erlang" /><category scheme="http://www.blogger.com/atom/ns#" term="xml" /><category scheme="http://www.blogger.com/atom/ns#" term="Newton" /><category scheme="http://www.blogger.com/atom/ns#" term="Einstein" /><category scheme="http://www.blogger.com/atom/ns#" term="alaska" /><category scheme="http://www.blogger.com/atom/ns#" term="concurrency" /><title>Everything in Moderation</title><content type="html">On the night of November 18th, 1849, lovable, bearded mountain man Grizzly Adams found himself stranded on the Alaskan tundra in his simple canvas tent, with nothing to do but wait out the blizzard raging all around him.  At such times Adams was wont to compose computer programs in his head.  Sometimes he would write the code on his notepad, and when next he found himself in a larger town, such as Fairbanks, he would have it telegraphed to the nearest computing facility.  On this particular night, however, he did something extraordinary; he conceived of a brilliant improvement over assembly language, until then the only option available to programmers on the Difference Engine MXVII.  His conception was the "C" programming language, which has dominated the programming world ever since (along with its minor variants, C++, C#, Java, Smalltalk and Lisp).  C is a coating of syntactic sugar over Difference Engine Assembler (DEA).  For example, C's "++" operator corresponds to the DEA &lt;tt&gt;ADD-YE-THE-NUMERAL-ONE-TO-THE-NUMERAL-IN-QUO&lt;/tt&gt; operation code (which was a real b***h to write, by all accounts).  C's other statement types translated readily into DEA, too.  For example, the "for" loop reduced 30 lines of DEA (on average) down to about 10 lines (on average) of C: a big savings in those days, when one had to write code by hand, telegraph it to a computing facility (at a penny a word!), and wait patiently for the results.&lt;br /&gt;&lt;br /&gt;Computer Science being the conservative field that it is, programming languages have changed little in the 158 years following C's inception.  A few small enhancements have been added by C's successors, but these have been relatively minor, and have upheld the "spirit" of C.  For example, Danish inventor Bjarne Stroustrup created "C++" by adding to C the concept of a "class", a convenient syntax for limiting the scope of global variables.  Meanwhile, other language designers took different tacks.  Whereas Stroustrup created a "higher-level" C with C++, Stanford professor John McCarthy created a "lower-level" C, which he named "Lisp", wherein one programs directly in the intermediate representation (aka "abstract syntax tree") that C compilers generate.  Similarly, Alan Kay's "Smalltalk" is a minor variation of Lisp: it represents the C abstract syntax tree using fewer parentheses.  Although Smalltalk and Lisp remain exceedingly close to C, they are the most daring of any new programming languages.  Perl, Ruby, ML, Haskell, Python, SNOBOL, and others all fall somewhere on the spectrum between C and Lisp/Smalltalk.&lt;br /&gt;&lt;br /&gt;Lest you worry that I'm suggesting progress in the field of programming languages has been glacial, fear not!  I commend heartily the &lt;i&gt;intentionally&lt;/i&gt; moderate pace of development.  It has been for a good cause, after all: ensuring that programmers do not misuse overly-powerful language features which they do not understand.  By limiting programming languages to the small set of primitive operations that Charles Babbage developed, over 160 years ago, we ensure that programmers only attempt those problems for which they are qualified.  Think of these intentional limitations on programming languages as being akin to those signs you see at amusement parks, consisting of a leering Technicolor elf with a big cartoon bubble saying, "You must be &lt;b&gt;this&lt;/b&gt; tall to ride &lt;i&gt;The Bombaster&lt;/i&gt;".  The intense feat of abstract thinking required to see how useful applications could be built from such low-level functionality acts as a sort of programmer's amusement park sign.  Weak programmers recognize when they're "too short" and stay off the ride (and stay out of trouble!)  This is, in fact, the cornerstone of the widespread high quality, stability and performance of modern software applications that we experience every day.  So do not worry, I'm not suggesting it &lt;i&gt;ever&lt;/i&gt; change!&lt;br /&gt;&lt;br /&gt;That said, it is interesting, purely as an exercise, to consider what scientific and technological breakthroughs from the last 158 years could be taken advantage of in a new programming language.  Let's see.&lt;br /&gt;&lt;br /&gt;First, our basic notions about &lt;i&gt;time&lt;/i&gt; have changed since 1849.  Back then, Einstein's theory of Special Relativity, and all of the subsequent work in modern Physics, was, of course, unknown.  Newtonian mechanics was the only thing Grizzly Adams would have known about, and that meant action-at-a-distance.  Although Newton would not have told Grizzly that everything happened simultaneously, he would have become downright confused trying to explain to Grizzly how multiple concurrent activities would &lt;i&gt;actually&lt;/i&gt; work.  Thus C, and its variants, have no easy way to represent simultaneous activities.  Everything happens in a single stream of time.  For example, if you want multiple things to happen simultaneously, you have to "fake it" by interleaving them in your code.  For example, take this simple email client written in C:&lt;pre class="code"&gt;TextBox toTxt = new TextBox("To: ", ...);&lt;br /&gt;TextBox ccTxt = new TextBox("CC: ", ...);&lt;br /&gt;TextBox cc2Txt = new TextBox("CCRider: ", ...);&lt;br /&gt;TextBox subjectTxt = new TextBox("Subject: ", ...);&lt;br /&gt;TextBox bodyTxt = new TextBox("", 24, ...);&lt;br /&gt;TextBox statusTxt = new TextBox("Status", ReadOnly, ...);&lt;br /&gt;Button sendButton = new Button("Send", &amp;doSend);&lt;br /&gt;Button cancelButton = new Button("Cancel");&lt;br /&gt;Button resetButton = new Button("Reset");&lt;br /&gt;Frame frame = new MainFrame("Email");&lt;br /&gt;frame.Add(toTxt, ccTxt, cc2Txt, ...);&lt;br /&gt;&lt;br /&gt;// ... imagine lots of gnarly code here ...&lt;br /&gt;&lt;br /&gt;private const ipated void doSend(Button b) &lt;br /&gt;{&lt;br /&gt;  String[] smtpHosts = Config["smtp-hosts"];&lt;br /&gt;  if (smtpHosts != null) {&lt;br /&gt;    for (smtpHostIP, port in LookupViaDNS(smtpHosts)) {&lt;br /&gt;      Socket sock = Socket.Open(smtpHostIP, port);&lt;br /&gt;      int err = sock.Send("ELHO", ...);&lt;br /&gt;      if (err != 0) {&lt;br /&gt;        statusTxt.Update(String.Format("send error {0}", strerror(err)));&lt;br /&gt;      }&lt;br /&gt;      // etc...&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;This code functions, but suffers from problems such as the entire user interface freezing while hosts are being looked up via DNS, connections opened, handshakes shook, the contents of the mail being sent, etc.  That could take a long time, and users get peevish when they cannot interact with an application for extended periods.&lt;br /&gt;&lt;br /&gt;Since Grizzly Adam's day, Albert Einstein (and some other physicists, too, I think), frustrated by the dearth of good email clients, spent time extending Newton's concept of time.  The details are far beyond the scope of this essay (or, for that matter, my own comprehension) but suffice it to say that they can all be distilled thusly: &lt;i&gt;everything is relative&lt;/i&gt;.  For example, if I am riding my skateboard down the freeway at 10mph, then an 18-wheel tractor-trailer rig bearing down on me from behind, at 60mph, will appear &lt;i&gt;to me&lt;/i&gt; to be going 50mph (if I should be smart enough to turn around and look, that is).  Of course, the driver of the truck perceives her truck to be moving at 60mph, and me to be moving at &lt;i&gt;negative&lt;/i&gt; 50mph.  Furthermore, the only way I'm even &lt;i&gt;aware&lt;/i&gt; of the tractor-trailer's presence is due to &lt;i&gt;signals&lt;/i&gt; of one sort or the other (light and sound) bouncing around between the truck and myself.  For example, I can &lt;i&gt;see&lt;/i&gt; the photons that have bounced my way from the gleaming chrome grille on the front of the truck, and I can &lt;i&gt;hear&lt;/i&gt; the desperate shrieking of the truck's horn as the driver attempts to avoid turning me (and my skateboard) into a pancake.&lt;br /&gt;&lt;br /&gt;To keep programmers from turning &lt;i&gt;themselves&lt;/i&gt; (and others) into &lt;i&gt;metaphorical&lt;/i&gt; pancakes, these two concepts (concurrent activities happening in their own space/time continuum, and the signals that can pass between them) have been kept strictly separate, &lt;i&gt;and&lt;/i&gt; have been kept rigorously out of any programming languages.  This has been accomplished through the tireless efforts of a secret, underground organization (the cryptically-named &lt;i&gt;Association for Moderation in Computing&lt;/i&gt; (AMC), a group about which little is known beyond their name and the few scant facts I am about to relay to you) that has maintained a long-standing disinformation campaign:&lt;ol&gt;&lt;li&gt;AMC agents have infiltrated all language development committees and have ensured that these groups allow concurrency and signaling primitives to exist &lt;b&gt;only in libraries&lt;/b&gt;, if indeed they exist at all.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;These same agents are also responsible for promulgating a false dichotomy between the two features, via strategically placed blogs, Usenet posts, blackmail and hallway conversations.  Calling concurrency "threads" and signals "events", the agents have, Jason-like, thrown a proverbial rock into the crowd of computer scientists.  Half of them are convinced that threads are evil and must be obliterated in favor of events, and the other half that the &lt;i&gt;exact opposite&lt;/i&gt; is true.  These camps war constantly with each other.  And while the AMC's real aim is to prevent use of &lt;i&gt;either&lt;/i&gt; feature, at least this way the damage is mitigated.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;With all of that as a caveat, one could imagine a language where this sort of everything-is-relative concept, as well as signaling, were &lt;i&gt;both&lt;/i&gt; built in &lt;i&gt;directly&lt;/i&gt; to the language.  To save me some typing in the remainder of this essay, I refer to this hypothetical language as &lt;i&gt;Relang&lt;/i&gt; (for &lt;b&gt;RE&lt;/b&gt;lative &lt;b&gt;LANG&lt;/b&gt;uage; get it?)  In Relang, anything logically concurrent could simply be coded that way, explicitly, without significant performance penalties.  Just as in real life, the only way these concurrent activities could interact with each other would be through "signals", sort of like the photons and sound waves that physicists have discovered since Grizzly Adam's time.  Each concurrent activity would probably have a data structure, such as a queue, to hold incoming signals, so that they could be processed in a comprehensible way.&lt;br /&gt;&lt;br /&gt;If we were to imagine the above example code re-written in this new language, each of the UI objects would exist in its own space/time continuum and communicate with other UI objects by sending signals (such as photons, sound waves, folded-up pieces of paper, and lewd gestures) to them.  One required change would be that lines like this:&lt;pre class="code"&gt;statusTxt.Update(String.Format("send error {0}", strerror(err)));&lt;/pre&gt;would now read:&lt;pre class="code"&gt;send(statusTxt, update, String.Format("send error {0}", strerror(err)));&lt;/pre&gt;&lt;br /&gt;Meanwhile, the statusTxt object would be sitting in a loop looking something like so:&lt;pre class="code"&gt;// This function is operating in its own&lt;br /&gt;// space/time continuum.&lt;br /&gt;static private isolated formless void superTextTNT()&lt;br /&gt;{&lt;br /&gt;  E = receive(infinity);&lt;br /&gt;  case E:&lt;br /&gt;    {update, txt} -&gt; self.txt = txt; send(mainFrame, update, self);&lt;br /&gt;    {invert} -&gt; self.invert(); send(mainFrame, update, self);&lt;br /&gt;    ...etc...&lt;br /&gt;  superTextTNT(); // this is a tail-call optimizing version of C&lt;br /&gt;}&lt;/pre&gt;Now it is clear that each UI element is "doing its own thing" in its own personal space, and not trying to grope any of the attractive UI elements sitting on neighboring barstools.  As a side-benefit, the UI would no longer freeze up all the time.&lt;br /&gt;&lt;br /&gt;Now, of course, you are probably thinking, "Ok, wise guy, why &lt;i&gt;don't&lt;/i&gt; languages work this way?"  Well, first off, this feature falls smack dab into the &lt;i&gt;too powerful&lt;/i&gt; category; it would be misused left, right, up, down, and sideways (hence the AMC's disinformation campaign).  Even were that not the case, however, there's this other problem.  To implement the above efficiently, the language would have to be &lt;i&gt;aware&lt;/i&gt; of when blocking (e.g., I/O) could occur.  Otherwise each of those "receive" statements would tie up a real OS thread, which is too expensive in the general case.  And so here is where the story takes a slightly sad turn.  Why &lt;i&gt;not&lt;/i&gt; make the programming aware of when blocking occurs?  Simple, because it is &lt;b&gt;impossible&lt;/b&gt; to do that.  While I am unsure of all the particulars, I have been assured by many top-notch computer scientist that that would be flat-out, 100%, totally, and absolutely, impossible.  No way in heck it could ever be implemented.  Oh well.&lt;br /&gt;&lt;br /&gt;On the bright side, while the language feature is unavailable, there is an easy work-around.  Use just &lt;i&gt;two&lt;/i&gt; threads of execution.  One creates a "user interface" (UI) thread and an "everything else" thread.  Then one ensures that &lt;i&gt;only&lt;/i&gt; UI-related activities occur on the UI thread and that, well, &lt;i&gt;everything else&lt;/i&gt; occur on the other.  In practice, this has turned out to be a model of simplicity (as anyone who has done UI programming could tell you, it is trivial to separate UI from "everything else") and is a big part of why most applications with graphical user interfaces work so flawlessly.  It &lt;i&gt;is&lt;/i&gt; unfortunate that one uses threads at all (they are deprecated for a reason, you know), but at least there are only two, and they can be implemented in a library, without polluting the language itself.  Further, because there are only two threads, signaling is kept entirely out of the picture; that extra bit of complexity is unnecessary with so few space/time continua to keep track of.  In fact, the &lt;i&gt;only&lt;/i&gt; downside is that two or more UI elements cannot update themselves simultaneously, since they are both operating on the UI thread.  Fortunately, I have never heard of a graphical application that actually required this.  Let us hope that such a beast never comes crawling out of the proverbial swamp.&lt;br /&gt;&lt;br /&gt;Speaking of UI, let us now consider another as-yet untapped technological advance: modern display technology.  Back when C was first invented, all programs had to be written down in long hand by the programmer, and then a telegraph operator had to manually translate it into Morse code (unless the programmer happened to be at the computing facility).  C's extreme terseness stems from this.  All languages inspired by C have kept true to its roots; they are easy to write longhand, too, just in case one has to write software without a modern display handy.  If we were to give up this requirement (a bad idea in practice, mind you), how might a language make use of this Faustian feature?&lt;br /&gt;&lt;br /&gt;Since the features that have been added to C (e.g., object-oriented programming) have focused on new ways to scope data, why not start using, say, color for scope, too?  For example:&lt;pre class="code"&gt;int &lt;span style="color:red;"&gt;i&lt;/span&gt; = 0;&lt;br /&gt;private explicit adults only int foo()&lt;br /&gt;{&lt;br /&gt;  return &lt;span style="color:red;"&gt;i&lt;/span&gt;++;&lt;br /&gt;}&lt;/pre&gt;This trivial example shows how one can avoid typing "static", and make it a little easier to see where the variable is used.  You could imagine more complicated uses of this, though:&lt;br /&gt;&lt;pre class="code"&gt;public mutable implicit void CrawlSites(String hostsFile)&lt;br /&gt;{&lt;br /&gt;  try {&lt;br /&gt;    Stream hostStream = &lt;span style="color:red;"&gt;File.Open&lt;/span&gt;(hostsFile, "r");&lt;br /&gt;    String host;&lt;br /&gt;    int port;&lt;br /&gt;    while (host, port = &lt;span style="color:purple;"&gt;Parse&lt;/span&gt;(hostStream)) {&lt;br /&gt;      sock = &lt;span style="color:blue;"&gt;Socket.Open&lt;/span&gt;(host, port);&lt;br /&gt;      &lt;span style="color:pink;"&gt;CrawlSite&lt;/span&gt;(sock);&lt;br /&gt;    }&lt;br /&gt;  } catch (&lt;span style="color:red;"&gt;IOError fileError&lt;/span&gt;) {&lt;br /&gt;    Blub.System.Console.ErrorStream.Writeln("unable to open hosts file: %s", fileError.toString());&lt;br /&gt;  } catch (&lt;span style="color:blue;"&gt;IOError socketError&lt;/span&gt;) {&lt;br /&gt;    MaybeThrottleFutureConnections(host, port);&lt;br /&gt;    Blub.System.Console.ErrorStream.Writeln("unable to connect to %s:%s: %s", host.toString(), port.toString(), socketError.toString());&lt;br /&gt;  } catch (&lt;span style="color:purple;"&gt;IOError parseError&lt;/span&gt;) {&lt;br /&gt;    Blub.System.Console.ErrorStream.Writeln("error reading from '%s': %s", hostsFile, parseError.toString());&lt;br /&gt;  } catch (&lt;span style="color:pink;"&gt;IOError socketReadError&lt;/span&gt;) {&lt;br /&gt;    Blub.System.Console.ErrorStream.Writeln("error reading from host '%s':%d: %s", host, port, socketReadError.toString());&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;One could do more than just set the color of the variables themselves.  For example, one could use the mouse to right-click on an entire line and set its background.  Say you want to make sure that nobody messes with the log object in the following function:&lt;pre class="code"&gt;private static cling void foo()&lt;br /&gt;{&lt;br /&gt;  &lt;span style="background-color: yellow;"&gt;Log log = new Log("foo starting")&lt;/span&gt;;&lt;br /&gt;  // ...imagine lots of stuff here...&lt;br /&gt;  &lt;span style="background-color: yellow;"&gt;log.End()&lt;/span&gt;;&lt;br /&gt;}&lt;/pre&gt;The rule would be that lines with the same background color would all be in a special sub-scope that was unavailable to anyone else.  If someone accidentally added in some code that referred to "log" in the middle of the big, hairy function, it would be flagged as an error.&lt;br /&gt;&lt;br /&gt;As stated earlier, this feature (and related features that would require a graphical user interface) is kept out of programming languages for a reason: we might have to start writing code without computers around, someday.  If languages themselves depended on a GUI, society would probably collapse while programmers scrambled to re-write everything in C or one of its variants.&lt;br /&gt;&lt;br /&gt;A friend of mine thought of a solution for this problem, but it is so far "out there", I'm only going to mention it in passing: one could use XML to "annotate" the hand-writable parts of code.  He noticed noticed this as he was typing in the examples such as the ones above, in fact: he was doing stuff like... [&lt;font color="red"&gt;Editor's note&lt;/font&gt;:  Ok, dammit, here's the problem.  I can't figure out how to "quote" XML in this dang blog.  It keeps getting interpreted as HTML, and so, while I'd like to show you what the code would look like the way I envision it, I'm going to have to hack around the problem.  Instead of actual XML, I'm going to use a mock XML that uses parentheses instead of less-than and greater-than.  It's also kind of late at night, and I'm getting tired, so I'm going to not type the end tags in.  Let's call this XML variant PXML, for &lt;i&gt;Parenthetical XML&lt;/i&gt;.]  The example above, in PXML, would look like:&lt;pre class="code"&gt;private static cling void foo()&lt;br /&gt;{&lt;br /&gt;  (color :yellow Log log = new Log("foo starting"));&lt;br /&gt;  // ...imagine lots of stuff here...&lt;br /&gt;  (color :yellow log.End());&lt;br /&gt;}&lt;/pre&gt;The way this would work, with PXML (again, sorry I can't write out the real XML as I would prefer), is that, if you were writing out the code using a pen and paper (or a typewriter, or the dusty teletype you came across in your company's storeroom while looking for a place to smoke) then you'd write out the annotations such as the "color" PXML form directly.  If you were using a modern IDE, on the other hand, the IDE wouldn't actually show you the PXML (well, not unless you actually &lt;i&gt;asked&lt;/i&gt; it to).  It would just show the whizzy colors (again, unless for some reason you &lt;i&gt;preferred&lt;/i&gt; the PXML).  And of course, you could use this mechanism for something more than just coloring and scope.&lt;br /&gt;&lt;br /&gt;For example, suppose you were a big Python fan.  You could edit your code in a sort of Python-looking mode.  Under the covers, the IDE would be generating PXML, but it wouldn't show that to you, after all, you're a Python fan, right?  I.e., if you wrote this in Python:&lt;pre class="code"&gt;def foo(a, b):&lt;br /&gt;  return a + b;&lt;/pre&gt;Then the IDE could just generate the following under the covers:&lt;br /&gt;&lt;pre class="code"&gt;(def foo (a b)&lt;br /&gt;  (return (+ a b)))&lt;/pre&gt;Of course, in order to let you write code using &lt;i&gt;any&lt;/i&gt; Python feature (list comprehensions, iterators, etc.), there would have to be some way for you to express those things in XML.  Fortunately, as we all know, XML can represent anything, so this would not be a problem.&lt;pre class="code"&gt;# Python&lt;br /&gt;(x * y for x in l1 for y in l2 if x % 2 == 0])&lt;/pre&gt;would be:&lt;pre class="code"&gt;(generator (* x y)&lt;br /&gt;  ((x l1))&lt;br /&gt;   (y l2))&lt;br /&gt; (= (mod x 2) 0))&lt;/pre&gt;&lt;br /&gt;[Again, &lt;i&gt;sorry&lt;/i&gt; for not being able to write out the actual XML, which would look a lot cooler; I know that PXML is painful to look at.  Darn blog!  Actually, I guess since there is no fundamental difference between XML and PXML, the IDE could probably use either one, interchangeably, under the covers.  But I doubt anyone in their right mind would actually &lt;i&gt;choose&lt;/i&gt; PXML over XML, so it would not be worth the extra time to implement that.]&lt;br /&gt;&lt;br /&gt;In any case, the AMC would look askance at &lt;i&gt;any&lt;/i&gt; of these proposed features, so let us resume thinking about another technology!&lt;br /&gt;&lt;br /&gt;Perhaps the biggest invention to come along has been the "Internet".  No longer are computers linked together via a tenuous chain of telegraph wires and human operators, as Grizzly Adams had to endure.  Modern computers are linked up more or less directly by a vast web of routers, operated not by humans but tiny elves who can move with great precision at near-light speed.  It would be incredibly dangerous to make this "Internet" into a first-class concept within a programming language.  Programmers would just share bad code with each other that much more easily!  Nevertheless, just for fun, let's look at how things might work if one &lt;i&gt;were&lt;/i&gt; to add such a dangerous feature.  For inspiration, let us consider a feature that Sun Microsystems playfully suggested via an in-joke, of sorts, in Java's package system. &lt;br /&gt;&lt;br /&gt;To use another company's Java package, Sun requires the following procedure:&lt;ol&gt;&lt;li&gt;The programmer mails in a form, ordering a magnetic tape containing the desired Java package from the appropriate vendor.&lt;br /&gt;&lt;li&gt;After a few weeks it arrives on a donkey cart, driven by a crusty old farmer.&lt;br /&gt;&lt;li&gt;The programmer tips the farmer generously (if they ever want another delivery).&lt;br /&gt;&lt;li&gt;The programmer must then use the cpio utility to remove the package from the tape.&lt;br /&gt;&lt;li&gt;At this point, the package (which is in a "jar" file, named for irrepressible &lt;i&gt;Star Wars&lt;/i&gt; character Jar Jar Binks) is intentionally not usable until you go through the further step of adding its location to an environment variable called the CLASSPATH.&lt;br /&gt;&lt;li&gt;The programmer is now &lt;i&gt;almost&lt;/i&gt; ready to use the package, provided that s/he restarts any Java software running on their machine, and did not mistype the path to the jar file.&lt;br /&gt;&lt;li&gt;It may also be helpful, at this point, for them to do a Navajo rain dance and sacrifice a chicken, especially if trying to get more than one "jar" file installed properly.&lt;/ol&gt;Now, the in-joke that I referred to above is the following: Sun's convention for &lt;i&gt;naming&lt;/i&gt; these packages is to use "Internet" addresses (well, actually, &lt;i&gt;reversed&lt;/i&gt; addresses, to make it more of an in-joke).  I think they were making a subtle reference to the unimplemented feature whereby this:&lt;pre class="code"&gt;import com.sun.java.jaaxbrpc2ee;&lt;/pre&gt;would cause the Java Virtual Machine to download the jaaxbrpc2ee package directly from "java.sun.com", without the programmer having to do anything.  For numerous reasons this would never work.  What if your computer weren't connected to this "Internet"?  Or what if you were to download an evil version written by evil hackers?    It is well-known that both of these problems are completely intractable, whereas the manual steps listed above &lt;i&gt;ensure&lt;/i&gt; nothing like this could ever happen.  Nonetheless, the direct downloading way does seem like it might speed up the pace of software development a bit!  Too bad there are all those problems with it.&lt;br /&gt;&lt;br /&gt;What if writing:&lt;pre class="code"&gt;import com.sun.java.jaaxbrpc2ee;&lt;/pre&gt;actually &lt;i&gt;did&lt;/i&gt; download, install, and, well, import the jaaxbrpc2ee package?  Ignoring the impossibility of actually making a socket connection, finding space on local disk, etc (more intractable problems, I'm afraid), I can think of two major issues with this:&lt;ol&gt;&lt;li&gt;Security&lt;br /&gt;&lt;li&gt;Versioning&lt;br /&gt;&lt;/ol&gt;Security would be a toughie.  You might have to have an MD5 or SHA1 hash of the Jar Jar file available somewhere for the JVM to check on in order to determine if it has downloaded a valid copy.  Not sure this would work, but if it did you could actually download the Jar Jar file from your cousin Harold's warez site that he runs out of his basement.   That way if com.sun.java was down you'd have at least one other option.&lt;br /&gt;&lt;br /&gt;Then there's the versioning issue.  What if you were developing against version 2.3.5 of jaaxbrpc2ee and Sun released version 3.0.0?  How would the JVM know not to use it?  Chances are, you'd have to add some syntax to Java in order to handle this.  You could use a regular expression to indicate which versions were acceptable:&lt;pre class="code"&gt;import com.sun.java.jaaxbrpc2ee 2.3.*;&lt;br /&gt;import org.asm.omygawd *.*.*.*;&lt;br /&gt;import org.standards.jdbc.odbc.dbase2 19.007a*;&lt;/pre&gt;&lt;br /&gt;Of course, as you can see, anyone can publish a package that others could download and use directly.  There could even be a free service that maintained these packages, for people who lacked the wherewithal to host their own packages.  They'd just have to prove they owned their domain name.  Also, people wouldn't be allowed to update an existing version, they'd have to release a new version.&lt;br /&gt;&lt;br /&gt;Ideally, one would be able to use the "Internet" anywhere in your code, not just when importing packages.  Pretty much anything could use the "Internet":&lt;pre class="code"&gt;com.att.bell-labs.int32 i = org.ieee.math.pi * (radius ^ 2);&lt;/pre&gt;For example, if you wanted to run code on another machine, you might say (in PXML with Relang extensions):&lt;pre class="code"&gt;(on com.megacorp.server11:90210&lt;br /&gt;  (if ...mumble... (send com.megacorp.server23:2001 '(d00d u suck))&lt;/pre&gt;The above would run the "if" expression on server11.megacorp.com (port 90210), and that code would sometimes send a message to yet another machine.  Of course, you could use variables instead of hard-coded machine names and ports.  Web servers could communicate with web browsers (if they supported PXML/Relang) like so:&lt;pre class="code"&gt;(import com.niftystuff.clock 1.*)&lt;br /&gt;&lt;br /&gt;(def clock ()&lt;br /&gt;  (register :clock)&lt;br /&gt;  (receive infinity&lt;br /&gt;    (update-thine-self (com.niftystuff.clock:draw)))&lt;br /&gt;  (clock))&lt;br /&gt;&lt;br /&gt;(def display-time ()&lt;br /&gt;  (while true&lt;br /&gt;    (sleep 1)&lt;br /&gt;    (send :clock 'update-thine-self)))&lt;br /&gt;;; imagine lots more code here...&lt;br /&gt;(html&lt;br /&gt;  (body&lt;br /&gt;    (on browser&lt;br /&gt;       (spawn clock)&lt;br /&gt;       (spawn display-time)&lt;br /&gt;       (do some other stuff))))&lt;/pre&gt;&lt;br /&gt;Ah, but it's getting late, and I can see a strange van parked outside, and my dog is acting nervous (and I have heard something about an AMC "wet" team.)  Maybe it is time to post this blog and go to bed!  The crazy language sketched out above, is, well, just that: crazy!&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/pNbtKXBV1PA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/503788244167924508/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=503788244167924508" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/503788244167924508?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/503788244167924508?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/pNbtKXBV1PA/pl2.html" title="Everything in Moderation" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>4</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/02/pl2.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEEGRH8yfyp7ImA9WB5REk4.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-6874747120206688856</id><published>2007-02-16T20:19:00.000-08:00</published><updated>2007-06-18T22:57:05.197-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-06-18T22:57:05.197-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="soda" /><category scheme="http://www.blogger.com/atom/ns#" term="smalltalk" /><category scheme="http://www.blogger.com/atom/ns#" term="cl" /><category scheme="http://www.blogger.com/atom/ns#" term="erlang" /><category scheme="http://www.blogger.com/atom/ns#" term="c" /><title>Losing Big Is Easy</title><content type="html">What's the connection between soda and the failure of programming languages?  I would have thought "zero" had I not recently stumbled across a wikipedia article about a little-known predecessor of Coca-Cola.  As it turns out, in 1879, a scrappy Norwegian immigrant named Jan Kjarti (who, incidentally, also coined the term "artificial sweetener") started a soda company.  His company, Slug Cola (the motto was "you'll love to Slug it down!"), produced a soda that cost a little more than Coke but which actually tasted much better (note that it didn't actually use artificial sweetener, either).  It's hard to even find out what it tasted like anymore, as none of the small but loyal customer base of Slug Cola are still alive.  Fortunately, a noted Boston historian and Slug researcher has found a few scattered journal entries and newspaper clippings from the era.  By all accounts drinking Slug, relative to drinking Coke, was something akin to a transcendent experience.  Of course, since you've heard of Coke and haven't heard of Slug, you can probably guess what happened: Coke kicked their ass.  One of Jan's protégés (one Richard Angel) later wrote a newspaper editorial entitled "Coke is Better", about the failure of their company.  It was both heartfelt and poignant, and included a frank assessment of why Slug Cola failed.  He pointed to the fact that, just as Slug was making a few gains in the marketplace, the US entered what became known as the "Soda Winter", a period of about 10 years where the public became irrationally convinced that carbonated water was bad for your stomach.  Coke survived this period relatively unscathed, in part aided by the fact that people felt that some of Coke's ingredients actually offset the purported stomach-damaging properties of carbonation.  Aside from that, and perhaps more importantly, Coke was cheaper to produce, allowing Coke to expand more rapidly into new markets.  Try as they might, the proprietors of Slug Cola just couldn't convince members of the public (who had invariably tasted Coke already) of Slug's merits.&lt;br /&gt;&lt;br /&gt;Well, that's most of the story, at least.  At the very end of the wikipedia article, the author points out a fact that, once I read it, I could have kicked myself for not having figured out myself.  I mean, it was a long article and went into all this detail about how the company failed.  And not once did it dawn on me, and this is the part the author pointed out, that the &lt;i&gt;name&lt;/i&gt; of the product might be the most significant factor in its downfall.  Slug!  Duh!  Of course nobody wants to drink something called Slug!  I mean, you can try to tell them that Slug refers to the concept of eagerly drinking (i.e., "SLUGging down") a transcendent carbonated beverage.  But I just don't think you can overcome first impressions.  And, let's face it, the first impression of Slug is of, well, a slimy, oozing insect.  Of course, I'm guessing all of you reading this were way ahead of me this whole time, so I bet you think I'm pretty dumb.  All I can say is, go read the article and you might get a sense for how I could have failed to notice the obvious.  The story just got so poignant, convoluted, and interesting, that I experienced a momentary lapse of common sense.&lt;br /&gt;&lt;br /&gt;Naturally I couldn't help, at this point, thinking about other poignant and convoluted stories that I've read about, and, like a flash, something that has probably been in my mind for years now hit me like a ton of proverbial bricks!  The reason why my 3 favorite programming languages failed is that their names sucked ass!  Just like Slug!  I don't know why this didn't hit me before, but it just seems glaringly obvious now.  What's more, I did a bit of research, and determined that there seems to be an almost inverse correlation between the coolness of a language's name and the coolness of the language itself.  This might explain why we're still writing code using the equivalent of blunt, hollowed-out turtle bones, bits of twigs and leaves and stuff, just like our primitive Neanderthal ancestors.&lt;br /&gt;&lt;br /&gt;If you don't believe me already, let's look at the all-time coolest programming language names:&lt;br /&gt;&lt;br /&gt;C.  Yup.  C.  C is cool.  It's mysterious.  It's cryptic.  It's one syllable.  It could even stand for Cool.  In my mind, there is no better name for a programming language, and never will be (any attempts to replicate its coolness by picking other, single letters of the alphabet, such as D, will come off as mere me-too-ism).  I remember in college, everyone wanted to know C.  People would ask questions in CS211 that they already knew the answer to, just so they could mention some feature of C that they'd learned.  I think the name has a lot to do with it.  The language itself was my favorite programming language for many years, until I finally started learning more about real programming languages.&lt;br /&gt;&lt;br /&gt;C++.  You can't improve upon the name C, but C++ was about as good as you can get.  I think C++ kicked the ass of its nearest competitor due to its name.  What would you rather use (if you didn't know anything about either language's technical merits): "C++" or "Objective-C".  Obj-jeck-tive-See.  Talk about clunky.  I write code in Ob-jeck-tive-See.  By the time you get to the third syllable you've just lost people's attention completely.  They'll be staring off in another direction asking questions like, "is there a breeze in here?"&lt;br /&gt;&lt;br /&gt;FORTRAN.  Yup.  You may hate it, but it sure is popular, even to this day.  FORTRAN always sounded cool to me, before I learned anything about it.  It sounds like "fortress", but with a cool sci-fi twist to it.&lt;br /&gt;&lt;br /&gt;Java.  Gotta hand it to Sun for picking a kick-ass name.  It's short, it's cryptic, it's friendly; it implies a connection with a caffeinated beverage, something near and dear to many programmers' hearts.  Its success tracked the explosive growth of upscale coffee shops, such as Starbucks.  Yep, Java's stratospheric success has much to do with its name.&lt;br /&gt;&lt;br /&gt;Now let's look at the all-time loser names.&lt;br /&gt;&lt;br /&gt;Coming in with the 3rd worst name for a computer language, of all time, is Erlang.  I write code in Ur-lang.  I am caveman.  I grunt and say Ur a lot.  After laughing at you for a while, anyone you're trying to convince of Erlang's merits next question is, "Why is it called ERlang?  Does it stand for Ericsson Language?"  You're pretty much sunk at this point.  It would be like if you were trying to get people to use MicrosoftLanguage instead of C#.  Wouldn't happen.  They'd just feel too silly.&lt;br /&gt;&lt;br /&gt;Coming in with the 2nd worst name of all time is...  Smalltalk.  This list just gets sadder as we get towards number one, doesn't it folks?   Because the languages just keep getting better.  Let me ask you this, folks: Smalltalk was designed by some of the best and brightest computer scientists of all time.  Numerous of them have won Turing awards and other accolades.  Why wasn't someone amongst them smart enough to point out this dead obvious fact: something called SMALLtalk is &lt;i&gt;never&lt;/i&gt; going to be successful?  I remember when I took Intro to Programming Languages and we were supposed to use SMALLtalk at one point.  I just couldn't believe it.  This is America, folks.  Bigger is better.  I wasn't about to use a puny, wimpy language that went so far as to point out its diminutive nature in its own name!  I remember I just suffered through the SMALLtalk portion of the course and didn't pay the slightest attention to any of the language's merits.  I mean, even if you ignore the SMALL aspect, what does "small talk" actually mean?  It refers to trivial, banal conversation.  Who wants to engage in banal conversation?  Does that mean the messages you send between objects are trivial and banal?  Sigh.  It only gets worse.  Smalltalk's latest incarnation is called Squeak.  That's right, it's named after the sound a small rodent/pest makes.  Might as well call it Slug.&lt;br /&gt;&lt;br /&gt;Last but certainly not least, here is the worst programming language name of all time: Lisp.  OMFG this is a bad name.  Bad bad bad.  What does "lisp" mean?  Lisp means "speech impediment"!!  Do you want a speech impediment!?  I don't think so!  Back in college I had even less patience for Lisp than I did for Smalltalk.  I mean, when it came down to it, I'd rather at least be able to make small talk, at a party, without having a lisp.  Hint to John McCarthy: next time you come up with something brilliant, name it after something POSITIVE.  Geez.  And, as with Erlang, it just doesn't get any better when you try to explain why Lisp is called Lisp.  It's short for LISt Processing...  Get it?  Isn't that funny?  I don't hear you laughing.  Yeah, 'cuz it's one of the worst puns ever.  And not only is it not funny, guess what, most programmers don't actually think they're going to &lt;i&gt;do&lt;/i&gt; much "list processing", so they're like, "maybe it IS good at List Processing, but I could give a flying monkey's posterior because that's just not what I want to be working on" (these programmers would much rather be working on their ORM layer, ironically).&lt;br /&gt;&lt;br /&gt;Phew.  So there it is folks.  One of the great tragedies of modern computer science turns out to have such a simple, prosaic explanation.  I would be more surprised that nobody else has ever mentioned this before, except that it took me 16 years and an obscure wikipedia article to see the light, so I guess I shouldn't expect anyone else to have done so, either.&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/cayGiGXytrQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/6874747120206688856/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=6874747120206688856" title="57 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6874747120206688856?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6874747120206688856?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/cayGiGXytrQ/losing-big-is-easy.html" title="Losing Big Is Easy" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>57</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/02/losing-big-is-easy.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0UFR3s_eyp7ImA9WBBaEU0.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-6541592005895889139</id><published>2007-01-17T21:07:00.000-08:00</published><updated>2007-01-17T22:20:16.543-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2007-01-17T22:20:16.543-08:00</app:edited><title>Living Software</title><content type="html">Steve Yegge's latest essay, &lt;a href="http://steve-yegge.blogspot.com/2007/01/pinocchio-problem.html"&gt;The Pinocchio Problem&lt;/a&gt; discusses how liveness, or the QWAN, is what makes software great.  Like many great ideas, his is a crystallization of something that one already knows (e.g., Emacs is uniquely amazing) but somehow cannot quite formalize.  In retrospect, of course, it seems almost obvious.  Now that I've read his essay, I can think of a few other pieces of software that are alive.&lt;br /&gt;&lt;br /&gt;One is Erlang, one of the two programming languages in existence that I wish I could use professionally but can't (the other is Common Lisp).  Erlang's live code migration feature allows upgrading code in a running system (such as an ATM switch, with tight availability requirements) in an extremely robust way.  As far as I know, no other programming language (even ones that support something like this, such as Common Lisp) have sufficient support built in to really be able to do this safely in production systems.  It also has a REPL, and is still the only reasonably well-supported language (i.e., one could consider using it without being laughed off the face of the planet) that actually tackles concurrency well.&lt;br /&gt;&lt;br /&gt;Another is, well, any serious relational database.  Although much maligned, SQL is actually a cool programming language.  And the development environment provided by the database is actually a REPL and is much better than your typical dead IDE.  Every time I have to do serious database programming, I actually find myself enjoying the interactivity of the experience.  Databases tackle some really thorny problems (such as persistence) that most programming languages treat as a secondary concern, unworthy of treatment directly in the language itself.  This is one of the major failings of most programming languages.  The C# team seems to be at least trying to remedy this with LINQ in C# 3.0, although I don't know too much about it.&lt;br /&gt;&lt;br /&gt;So I guess in addition to the qualities that Steve mentions that make software alive, I would add support for &lt;b&gt;concurrency&lt;/b&gt; (or, more generally, explicit support for dealing with the passage of time), and &lt;b&gt;persistence&lt;/b&gt;.  There are probably a few other qualities as well (such as dealing with uncertainty, but it's too late at night to write any more about that).&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/yFpjTvetXzw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/6541592005895889139/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=6541592005895889139" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6541592005895889139?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/6541592005895889139?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/yFpjTvetXzw/living-software.html" title="Living Software" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>0</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2007/01/living-software.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0IMSH44cSp7ImA9WxZVEUk.&quot;"><id>tag:blogger.com,1999:blog-32049549.post-115527048645936465</id><published>2006-10-28T21:12:00.000-07:00</published><updated>2008-03-21T16:19:49.039-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-03-21T16:19:49.039-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="poo" /><category scheme="http://www.blogger.com/atom/ns#" term="oop" /><title>How poopy is YOUR code?</title><content type="html">After numerous years at the top, &lt;i&gt;object-oriented&lt;/i&gt; still reigns as the number-one programming buzzword (this claim is based on a wide-ranging, highly scientific, double-blind study of my opinion on the subject). I find this interesting because, in my observation, programmers rarely use OOP. They may use OO &lt;em&gt;languages&lt;/em&gt;, but your typical chunk of &lt;em&gt;code&lt;/em&gt; is rarely terribly OOP-y.  Despite this, there has been an obsession with OOP over the last 20 years that has possibly obscured more significant techniques.&lt;br /&gt;&lt;br /&gt;Even the experts do not agree on what OOP truly is, but it is most&lt;br /&gt;often associated with the following:&lt;ul&gt;&lt;li&gt;Subclassing&lt;br /&gt;&lt;li&gt;Interfaces&lt;br /&gt;&lt;li&gt;Polymorphism&lt;br /&gt;&lt;li&gt;Encapsulation&lt;br /&gt;&lt;/ul&gt;All of these things are programming techniques, rather than inherently desirable qualities.  So why do we spend all our time writing stuff like &lt;tt&gt;ProtocolFactory.Instance().Create(new AddressAdapter(anAddress, aPort))&lt;/tt&gt;?  In a word, &lt;i&gt;maintainability&lt;/i&gt;.  In case you haven't heard, &lt;i&gt;maintainable&lt;/i&gt; code is defined as "code I like".  "Your code isn't maintainable," should you ever hear it, usually means, "I don't like your code but I'm not sure why exactly and/or I don't feel like explaining myself."  (If someone is really unhappy with your code, rather than say it's unmaintainable, they'll say that it "doesn't scale").  Cynics would point out, here, that 20 years of struggling to use OO techniques correctly has been motivated by solely something this subjective.  Of course, I don't agree with these cynics.&lt;br /&gt;&lt;br /&gt;In my experience, OO has been useful for a few things.  For one thing, it's a way to see if a programmer can understand abstract concepts such as subclassing, not only because they will periodically stumble across a use for them, but also because it provides a sense of how well they'll be able to learn all the &lt;i&gt;other&lt;/i&gt; tools that they'll need to do their job.  I.e., being able to learn to use programming techniques, in general, is a useful skill in and of itself.  As with anything, though, simply using tools doesn't necessarily mean they're being used correctly or appropriately.  In fact, in programming, emphasis on techniques has not been accompanied by a serious focus on why those techniques should be used.  See, &lt;i&gt;that&lt;/i&gt; problem is considered too subjective.  It's easier to give the same tired example of an &lt;b&gt;Animal&lt;/b&gt; class hierarchy than it is to explain &lt;i&gt;why&lt;/i&gt; polymorphism should be used to make good software in some real-life case.&lt;br /&gt;&lt;br /&gt;Fortunately, I don't think it has to be that hard or subjective.  So enough theorizing, for now, let's get down to a real example.  What is the &lt;i&gt;biggest&lt;/i&gt; problem with the following code?&lt;pre class="code"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Socket.h&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="type"&gt;Socket&lt;/span&gt; {&lt;br /&gt;  ...mumble...&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="function-name"&gt;CreateSocket&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;Open&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;, &lt;span class="type"&gt;Address&lt;/span&gt; &lt;span class="variable-name"&gt;addr&lt;/span&gt;, &lt;span class="type"&gt;Port&lt;/span&gt; &lt;span class="variable-name"&gt;port&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Returns number of bytes actually read.&lt;br /&gt;&lt;/span&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="function-name"&gt;Read&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;, byte[] outBuffer);&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Returns number of bytes actually written.&lt;br /&gt;&lt;/span&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="function-name"&gt;Write&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;, byte[] buffer);&lt;br /&gt;&lt;br /&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;Close&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;);&lt;/pre&gt;(Ok, no points to the wiseacre who said, "It's written in Blub.")&lt;br /&gt;&lt;br /&gt;One could point out that it's not very OOP-y.  It doesn't use encapsulation (at least not officially), in that the &lt;b&gt;Socket&lt;/b&gt; struct is just a plain old struct.  It has no class definition, and doesn't implement an interface.  It also doesn't make use of polymorphism!  What if I want to be able to pass this &lt;b&gt;Socket&lt;/b&gt; to something that expects a &lt;b&gt;Stream&lt;/b&gt;?&lt;br /&gt;&lt;br /&gt;These might be valid concerns in one situation or another, but remain, nonetheless, mostly mechanical issues.  The big problem is that this code is difficult to use correctly.  Worst of all, it's difficult to debug.  For example, you can write this:&lt;pre class="code"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;More pseudo-C&lt;br /&gt;&lt;/span&gt;&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt; = CreateSocket();&lt;br /&gt;s.Write(myBuffer);&lt;/pre&gt;In this case &lt;b&gt;Open()&lt;/b&gt; was never called, but to figure out that this is a problem requires diagnosing a mysterious failure at run-time, which may not be easy to track down to its source.  And while the above is a rather trivial case that you'd likely notice simply by looking at it, there are more insidious and realistic examples, such as:&lt;pre class="code"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;Handshake&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;)&lt;br /&gt;{&lt;br /&gt;   &lt;span class="keyword"&gt;const&lt;/span&gt; &lt;span class="type"&gt;byte&lt;/span&gt;[] header = { 0xBEEF, 0xBEEF };&lt;br /&gt;   &lt;span class="keyword"&gt;if&lt;/span&gt; (s.Write(header) != header.length) {&lt;br /&gt;      ...mumble...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;The following is in a file/library far, far away&lt;br /&gt;&lt;/span&gt;...&lt;br /&gt;&lt;span class="type"&gt;void&lt;/span&gt; Mumble(...)&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;   Socket s1 = Socket();&lt;br /&gt;   Open(s1, &lt;span class="string"&gt;"www.google.com"&lt;/span&gt;, 80);&lt;br /&gt;   &lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s2&lt;/span&gt; = Socket();&lt;br /&gt;   Open(s1, &lt;span class="string"&gt;"localhost"&lt;/span&gt;, 2341); &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;OOPS!&lt;br /&gt;&lt;/span&gt;   Handshake(s1);&lt;br /&gt;   Handshake(s2); &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;BOOM!&lt;br /&gt;&lt;/span&gt;   ...&lt;br /&gt;}&lt;/pre&gt;This could easily happen, just a slip of the finger while typing. Since this code is written in C, the results will be catastrophic and hard to diagnose.  This code is like a city sidewalk filled with open manholes; you can avoid them, but you have to watch where you step. And did I mention you're in one of those large, cold, unfriendly cities where your fellow pedestrians are unlikely to warn you of your imminent demise?&lt;br /&gt;&lt;br /&gt;This does actually relate to OOP.  The problem is that programmers are taught all about how to write OO code, and how doing so will improve the &lt;i&gt;maintainability&lt;/i&gt; of their code.  And by "taught", I don't just mean "taken a class or two".  I mean: have pounded into head in school, spend years as a professional being mentored by senior OO "architects" and only then finally kind of understand how to use properly, some of the time.  Most engineers wouldn't consider using a non-OO language, even if it had amazing features ... the hype is that major.&lt;br /&gt;&lt;br /&gt;So what, then, about all that code programmers write before their &lt;a href="http://www.norvig.com/21-days.html"&gt;10 years&lt;/a&gt; OO apprenticeship is complete?  Is it just doomed to suck?  (Well, obviously I don't think so becuase, if I didn't, I wouldn't have been writing this essay.)  Of course not, as long as they apply other techniques than OO.  These techniques are out there but aren't as widely discussed.  Going back to the example, here's an improved version.&lt;pre class="code"&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Socket.h&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;enum&lt;/span&gt; &lt;span class="type"&gt;MagicNumbers&lt;/span&gt; {&lt;br /&gt;   &lt;span class="variable-name"&gt;OpenSocket&lt;/span&gt; = 0xE474BEEF,&lt;br /&gt;   &lt;span class="variable-name"&gt;ClosedSocket&lt;/span&gt; = 0xBEEF4A11,&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="type"&gt;Socket&lt;/span&gt; {&lt;br /&gt;  ...&lt;br /&gt;  MagicNumber magicNumber;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;Open&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;, &lt;span class="type"&gt;Address&lt;/span&gt; &lt;span class="variable-name"&gt;addr&lt;/span&gt;, &lt;span class="type"&gt;Port&lt;/span&gt; &lt;span class="variable-name"&gt;port&lt;/span&gt;)&lt;br /&gt;{&lt;br /&gt;   ...&lt;br /&gt;   s.magicNumber = OpenSocket;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Returns number of bytes actually read.&lt;br /&gt;&lt;/span&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="function-name"&gt;Read&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;, byte[] outBuffer) &lt;br /&gt;{&lt;br /&gt;   Assert(s.magicNumber != ClosedSocket, &lt;span class="string"&gt;"you already Closed that Socket!"&lt;/span&gt;);&lt;br /&gt;   Assert(s.magicNumber == OpenSocket, &lt;span class="string"&gt;"using never-Opened or corrupt Socket!"&lt;/span&gt;);&lt;br /&gt;   ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;Returns number of bytes actually written.&lt;br /&gt;&lt;/span&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="function-name"&gt;Write&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;, byte[] buffer) &lt;br /&gt;{&lt;br /&gt;   ...same stuff as Read...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="function-name"&gt;Close&lt;/span&gt;(&lt;span class="type"&gt;Socket&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt;)&lt;br /&gt;{&lt;br /&gt;   Assert(s.magicNumber == OpenSocket, &lt;span class="string"&gt;"attempt to Close an unopened Socket!"&lt;/span&gt;);&lt;br /&gt;   ...&lt;br /&gt;}&lt;/pre&gt;The improvement here has little to do with any specific programming technique (in fact, there are better ways to implement the change). It's more a matter of &lt;i&gt;empathy&lt;/i&gt;; in this case, for the programmer who might have to use your code.  The author of this code actually thought through what kinds of mistakes another programmer might make, and strove to make the &lt;i&gt;computer&lt;/i&gt; tell the programmer what they did wrong.&lt;br /&gt;&lt;br /&gt;In my experience the best code, like the best user interfaces, seems to magically anticipate what you want (or need) to do next.  Yet it's discussed infrequently relative to OO.  Maybe what's missing is a buzzword.  So let's make one up, &lt;i&gt;Programming fOr Others&lt;/i&gt;, or &lt;i&gt;POO&lt;/i&gt; for short.&lt;br /&gt;&lt;br /&gt;One good example of POO in action is &lt;a href="http://sqlite.org"&gt;sqlite&lt;/a&gt;.  One of the many relational database technologies out there, this one distinguishes itself by &lt;i&gt;just working&lt;/i&gt;.  I downloaded it and ran it without any configuration and it just kind of did what I expected it to, gave me a SQL prompt.  You'd think this would be obvious, but setting up most databases is far from trivial, involving setting up users, passwords, config files, etc.  It's as if the sqlite programmers actually read &lt;i&gt;&lt;a href="http://www.graficaobscura.com/future/futnotes.html"&gt;Futurist Programming Notes&lt;/a&gt;&lt;/i&gt;.  Sqlite doesn't restrict its poopy behavior to startup.  When you create a table, for example, you don't have to specify the type of each row.  It just lets you put in whatever the heck you want.  Of course, you &lt;i&gt;may&lt;/i&gt; specify the type if you &lt;i&gt;want&lt;/i&gt; to, but the point is that the creators of this fine piece of software actually realized that you just might not care.&lt;br /&gt;&lt;br /&gt;By contrast, there are numerous software packages and APIs that aren't, frankly, very POO.  For example, take your typical, basic IO API:&lt;pre class="code"&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="function-name"&gt;read&lt;/span&gt;(&lt;span class="type"&gt;File&lt;/span&gt; &lt;span class="variable-name"&gt;f&lt;/span&gt;, &lt;span class="type"&gt;char&lt;/span&gt; *&lt;span class="variable-name"&gt;buffer&lt;/span&gt;, &lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="variable-name"&gt;bytesToRead&lt;/span&gt;);&lt;br /&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="function-name"&gt;write&lt;/span&gt;(&lt;span class="type"&gt;File&lt;/span&gt; &lt;span class="variable-name"&gt;f&lt;/span&gt;, &lt;span class="type"&gt;char&lt;/span&gt; *&lt;span class="variable-name"&gt;buffer&lt;/span&gt;, &lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="variable-name"&gt;bytesToWrite&lt;/span&gt;);&lt;/pre&gt; This code has a major (albeit subtle) issue: the values chosen for &lt;b&gt;bytesToRead&lt;/b&gt; and &lt;b&gt;bytesToWrite&lt;/b&gt; may have serious performance implications.  But how do you even know that in the first place?  And once you do, what do you do about it?  You have little choice but to conduct a series of laborious experiments to figure out the best buffer size to use in each particular case.  Maybe on some machines it's the size of a page, maybe on others a different size. And of course it might change with the next revision of your operating system, etc.&lt;br /&gt;&lt;br /&gt;At this point you may be wondering, "Gee, so it's a little hard to use, what's the big deal?"  Well, imagine 5 people were standing around a room talking, and one of them wanted to know what 3.1451515 * 92.003 was?  Now I haven't told you one critical piece of information, which is that there is, in addition to the 5 people already mentioned, a &lt;i&gt;computer&lt;/i&gt; in the corner of the room.  Given all this, what would you expect the people to do:&lt;ul type="a"&gt;&lt;li&gt;Bust out some paper and pencils and try to remember how to do long division by hand, or,&lt;li&gt;Use the computer?&lt;/ul&gt;This wasn't a hard question to answer.  Yet, for some reason, in almost the exact same situation (ok, not the &lt;i&gt;exact&lt;/i&gt; same situation, but closer than most people would like to admit), programmers tend to leave a problem (such as figuring out optimal buffer sizes) to (human) programmers rather than let computers figure it out.&lt;img src="http://feeds.feedburner.com/~r/blogspot/xuFvSR/~4/UGw6A94YsGM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://a-nickels-worth.blogspot.com/feeds/115527048645936465/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=32049549&amp;postID=115527048645936465" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/115527048645936465?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/32049549/posts/default/115527048645936465?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/blogspot/xuFvSR/~3/UGw6A94YsGM/eop.html" title="How poopy is YOUR code?" /><author><name>Jacob Gabrielson</name><uri>http://www.blogger.com/profile/13887274100244616103</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="33" height="25" src="http://2.bp.blogspot.com/_f1Ze4TqLTW8/SfdEqnf2oeI/AAAAAAAAAOo/U7nx1qyKlnU/S220/Capture.PNG" /></author><thr:total>3</thr:total><feedburner:origLink>http://a-nickels-worth.blogspot.com/2006/08/eop.html</feedburner:origLink></entry></feed>
