<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Andreas Fuchs' Journal]]></title>
  <link href="http://boinkor.net/atom.xml" rel="self"/>
  <link href="http://boinkor.net/"/>
  <updated>2013-01-19T22:10:10-08:00</updated>
  <id>http://boinkor.net/</id>
  <author>
    <name><![CDATA[Andreas Fuchs]]></name>
    <email><![CDATA[asf@boinkor.net]]></email>
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Elixir: First Impressions]]></title>
    <link href="http://boinkor.net/archives/2013/01/elixir-first-impressions.html"/>
    <updated>2013-01-19T20:31:00-08:00</updated>
    <id>http://boinkor.net/archives/2013/01/elixir-first-impressions</id>
    <category term="Hacks" />
    <content type="html"><![CDATA[<p>For the longest time now, I&#8217;ve admired <a href='http://www.erlang.org/'>Erlang</a> from afar. It always seemed to be a bit daunting to take on. For one, there was the slightly weird and inconsistent Prolog-inspired syntax (I was always scratching my head over why <em>this</em> place needs a period and <em>that</em> place doesn&#8217;t), and then there was just plain weird stuff like one-based indexes.</p>

<p>While you don&#8217;t end up needing indexes very often, a nice syntax on top of Erlang is something I always kind of wanted, but nothing really could deliver. Then I saw Jose Valim demoing <a href='http://elixir-lang.org/'>Elixir</a> at <a href='https://thestrangeloop.com/archive/2012'>Strange Loop 2012</a>. It has a ruby-inspired (but more regular) syntax, it can do <a href='http://elixir-lang.org/getting_started/5.html'>macros</a>(!), it has <a href='http://elixir-lang.org/getting_started/4.html'>protocols</a>(!!!), and it has a very enthusiastic developer community behind it (see <a href='http://expm.co/'>expm</a> for an example of the packages that people have written/ported over to Elixir). That its data structures use zero-based index access certainly helps, too (-:</p>

<p>On top of all these nice things, it also lets you use any Erlang library (with only minimally less nice syntax by default). I think I&#8217;m sold.</p>

<h2 id='what_is_all_that_hair_on_the_floor'>What is all that hair on the floor?</h2>

<p>As an initial just-for-fun project, I tried porting over the progress I&#8217;d made on a node.js-based gmail-&gt;localhost IMAP backup tool that I&#8217;d optimistically named gmail-syncer.<sup id='fnref:1'><a href='#fn:1' rel='footnote'>1</a></sup> So far, this has required a ton of <a href='http://projects.csail.mit.edu/gsb/old-archive/gsb-archive/gsb2000-02-11.html'>yak shaving</a>, but I&#8217;m enjoying the hell out of every single step down the fractal yak ranch.</p>

<ul>
<li>
<p>First, there is no suitable IMAP client library. The thing that comes closest is <a href='http://code.google.com/p/erlmail/'>erlmail</a>. It is somewhat abandoned, and its IMAP client isn&#8217;t very usable for my purposes (doesn&#8217;t implement capabilities the way I need them, doesn&#8217;t really follow the <a href='http://dovecot.org/imap-client-coding-howto.html'>one relatively sane guide to writing an IMAP client</a>). So I&#8217;ll have to write my own IMAP interaction code.</p>
</li>

<li>
<p>To write my own IMAP code, I need to parse server responses; this requires parsing the highly weird IMAP protocol, with its somewhat lisp-inspired (but definitely not lispy) ideas of how to represent things. For example, The way a UID FETCH response looks makes it pretty impractical to tokenize &amp; parse the response using a parser generator - unless you enjoy concatenating potentially dozens of megabytes of text that would do better to remain as an opaque binary buffer.</p>
</li>

<li>
<p>Hence, to parse server responses in a smarter way, I have to have a smarter parser. While that can use a pretty nice heuristic (despite its lispy nature, the IMAP server responses are specified to terminate in newlines at certain points), I still need it to cooperate well with something that manages buffers received from the network somewhat smartly. Aaaand that&#8217;s where I am right now.</p>
</li>
</ul>

<p>Introducing <a href='https://github.com/antifuchs/gmail_synchronize'>gmail_synchronize</a>, the tool that doesn&#8217;t do very much right now other than fill a buffer and let you read lines or N-byte-long binaries from them. But I&#8217;m sure there will be more stuff eventually (-:</p>

<p>To come this far, I&#8217;ve written some kilobytes of code (on various levels of the aforementioned yak stack) and thrown them away. The results in the git repo are the best I have come up with, so far. This isn&#8217;t much, and so you should take the following opinions with a mine of salt.</p>

<h2 id='my_impression_of_elixir_so_far'>My impression of Elixir so far</h2>

<p>Here&#8217;s a brain dump of what about the language stood out to me:</p>

<p>So far, I really like Elixir (and, by extension, Erlang). There&#8217;s a lot to be said about its pattern matching (which is as powerful as <a href='http://learnyousomeerlang.com/syntax-in-functions'>Erlang&#8217;s</a>), but I don&#8217;t think I fully understand it yet. There&#8217;s a bit of terminology I still have to learn, but even at this level of (non-)proficiency, it&#8217;s making my job way easier.</p>

<p>There&#8217;s a very helpful channel on freenode, <a href='irc://irc.freenode.net/#elixir-lang'>#elixir-lang</a>. It has the creator of the language in it, and a bunch of very enthusiastic, knowledgeable and helpful people (hi, yrashk and cmn!). This has been invaluable in my learning to use the language.</p>

<p>I still don&#8217;t quite get why some of the decisions in it were made the way they were made. For example, it would seem natural to me to have a way to pattern-match binary buffers to test whether some bytes appear next to each other in the buffer, but there isn&#8217;t. I guess this may have to do with being able to unambiguously resolve the pattern, but it&#8217;s still a bit unsatisfactory. I&#8217;m sure this will pass as I learn more of its vocabulary and integrate it into mine.</p>

<p>Testing in Elixir is very cool. Instead of mocking or stubbing things like I would in, say, Ruby, I factor things such that tests can implement a protocol that the part being tested uses, and I&#8217;m set. I love <a href='http://www.amazon.com/Art-Metaobject-Protocol-Gregor-Kiczales/dp/0262610744'>protocols</a>, and I think Elixir lets you use them in a very nice way. See <a href='https://github.com/antifuchs/gmail_synchronize/blob/77fbca779588a0d92f0a18395e4149ff309722df/test/network_test.exs#L8-L25'>here</a> for how the tests interact with a library that follows a protocol. Note the re_buffered variable - in Ruby, I&#8217;d be using a <a href='http://gofreerange.com/mocha/docs/Mocha/Expectation.html#times-instance_method'>method call expectation</a> instead - this is way more satisfying.</p>

<p>Non-modifiable data structures are way less of a pain than I&#8217;d imagined (they are in fact pretty pleasing). The pattern matching makes things much easier to follow, and the way updates (which return a new object) work is also pretty cool: You can write stuff like: <figure class='code'><figcaption><span /></figcaption><div class='highlight'><table><tr><td class='gutter'><pre class='line-numbers'><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class='n'>some_record</span><span class='o'>.</span><span class='n'>buffer</span><span class='p'>(</span><span class='s2'>&quot;foo&quot;</span><span class='p'>)</span><span class='o'>.</span><span class='n'>number</span><span class='p'>(</span><span class='mi'>20</span><span class='p'>)</span>
</span></code></pre></td></tr></table></div></figure> &#8230;and this returns a record that is like some_record, except its <code>buffer</code> and <code>number</code> components are replaced by the values passed in the function argument list. Pretty pleasing.</p>

<p>I would not have been able to write code so relatively painlessly if it weren&#8217;t for the <a href='https://github.com/antifuchs/elixir-mode'>emacs mode</a> that I&#8217;ve painfully adjusted to automatically indent Elixir code correctly. Emacs&#8217;s <a href='http://www.gnu.org/software/emacs/manual/html_node/elisp/SMIE.html'>smie</a> is really pretty cool, and I wish more emacs modes used it (-:</p>

<p>That&#8217;s all so far. I urge you to check out Elixir, and hope you have as much fun with it as I do!</p>
<div class='footnotes'><hr /><ol><li id='fn:1'>
<p>Why write a new tool over using <a href='http://offlineimap.org/'>offlineimap</a>? Offlineimap is a huge pain - when used with gmail, it&#8217;ll sometimes run into UIDVALIDITY mismatches (which require a re-download of potentially huge mailboxes, which run for days), it&#8217;s slow, and its thread-based design is so horrible that it manages to mess up its own UI even when using a single worker thread, and then it can&#8217;t even exit cleanly on anything other than a SIGKILL. Arrrrgh.</p>
<a href='#fnref:1' rev='footnote'>&#8617;</a></li></ol></div>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Write gmail filters in a nice Ruby DSL: gmail-britta]]></title>
    <link href="http://boinkor.net/archives/2012/12/write-gmail-filters-in-a-nice-ruby-dsl-gmail-britta.html"/>
    <updated>2012-12-26T19:16:00-08:00</updated>
    <id>http://boinkor.net/archives/2012/12/write-gmail-filters-in-a-nice-ruby-dsl-gmail-britta</id>
    <category term="Hacks" />
    <content type="html"><![CDATA[<p>I&#8217;ve just finished (mostly) documenting and writing tests for my latest little library, <a href='https://github.com/antifuchs/gmail-britta'>gmail-britta</a>, so thought I should release it to the world as a sort of holiday gift.</p>

<p><a href='https://github.com/antifuchs/gmail-britta'>Gmail-britta</a> is a library that lets you write Gmail filters (hah, Britta, get it?) in a way that doesn&#8217;t drive you insane - you write them as a ruby program, run that program, and out comes XML that you can import into <a href='https://mail.google.com/mail/u/0/?shva=1#settings/filters'>Gmail&#8217;s filter settings</a>.</p>

<p>It does a bunch of other nice things, but I guess it&#8217;s better to let the <a href='https://github.com/antifuchs/gmail-britta/blob/master/README.md'>README explain</a></p>

<p>So far, I (and a few colleagues of mine) have been successfully using this for the past few months to generate filters for <a href='http://blog.alexmaccaw.com/stripes-culture'>work email</a>. Just yesterday I took the step and ported my <a href='https://twitter.com/antifuchs/status/283753876807614464'>156 filters</a> over to a <a href='https://github.com/antifuchs/gmail-britta/blob/master/examples/asf.rb'>gmail-britta program (yep, that&#8217;s my filters, with sensitive email addresses stubbed out)</a>, resulting in <a href='https://twitter.com/antifuchs/status/283754273240674304'>34, easier to maintain, more accurate filters</a>.</p>

<p>If you&#8217;re interested, please <a href='http://rubygems.org/gems/gmail-britta'>give it a try</a>. Also, <a href='https://github.com/antifuchs/gmail-britta/issues'>please let me know in the issues</a> if you find anything that it doesn&#8217;t do, or if you&#8217;re feeling super generous, <a href='https://github.com/antifuchs/gmail-britta/pulls'>please open a pull request and send me improvements</a>!</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Some more updates]]></title>
    <link href="http://boinkor.net/archives/2012/09/some-more-updates.html"/>
    <updated>2012-09-09T23:43:00-07:00</updated>
    <id>http://boinkor.net/archives/2012/09/some-more-updates</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>So I&#8217;ve been moving stuff off my 6 year old server to a machine hosted in Germany lately. I hope to bring back Boinkmarks on it some day soon. (Not in the way I <a href='http://boinkor.net/archives/2012/09/git-lives-again-somewhere-else.html'>brought back the git repos</a>, though - no outsourcing for benchmarks!) (-:</p>

<p>There are a couple state changes in my projects that would not warrant a blog post on their own, but I think as a whole are still something to write home about:</p>

<ul>
<li>
<p>I&#8217;m retiring the <a href='http://jofr.li/'>Jofr.li</a> web site - it pretty much got obsoleted at birth by Twitter&#8217;s URL shortening thing, and it was just a finger exercise anyway. You can still peek at the source if you want to get a feel for how I think a lisp redis-backed hunchentoot app could be structured.</p>
</li>

<li>
<p>The <a href='https://github.com/antifuchs/cxml-rpc'>CXML-RPC</a> library&#8217;s server part should now work in the newest Hunchentoot. In the process, I think I found a bug in CXML&#8217;s <a href='http://common-lisp.net/project/cxml/klacks.html'>Klacks</a> parser under <a href='http://clozure.com/clozurecl.html'>CCL</a> - it <a href='http://lists.common-lisp.net/pipermail/cxml-devel/2012-September/000582.html'>fails to decode &amp;# entities</a>.</p>
</li>

<li>
<p>In not so very lisp-related news, I am learning that keeping your server&#8217;s configuration in <a href='http://puppetlabs.com/'>puppet</a> is a really great thing. I&#8217;d never done this for my own machines up until this point, but it definitely helps to have all the state re-bootstrapable in one repository. Makes it way easier to reason about system configuration (&#8220;Now where are these vhosts&#8217; www root directories again? What, they were behind a bind mount? What was I thinking?!&#8221; - these moments are severely reduced when you can just look at git log output).</p>
</li>
</ul>

<p>And lastly, <a href='http://lispm.dyndns.org/dlw'>Dan Weinreb died</a>. I wish I had had more opportunities to work with him, chat with him and learn from him.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Git lives again - somewhere else]]></title>
    <link href="http://boinkor.net/archives/2012/09/git-lives-again-somewhere-else.html"/>
    <updated>2012-09-09T23:36:00-07:00</updated>
    <id>http://boinkor.net/archives/2012/09/git-lives-again-somewhere-else</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>I&#8217;ve revived the git repos affected by <a href='http://boinkor.net/archives/2012/08/git-dot-boinkor-dot-net-outage.html'>this outage</a> - the cvs-&gt;git conversion is now alive again, and the repos there are now kept on <a href='https://github.com/antifuchs/'>github.com</a>.</p>

<p>Turns out there are only two more CVS repos left that I was converting to git: <a href='https://github.com/antifuchs/mcclim'>McCLIM</a> and <a href='https://github.com/antifuchs/slime'>SLIME</a>. So, they&#8217;re online again, and I hope you still find them useful.</p>

<p>If you are missing any repos that I forgot to move, please send me a note. I should have extensive backups of everything, so restoring anything that&#8217;s missing probably isn&#8217;t a problem. (Famous last words, hah!)</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[git.boinkor.net outage]]></title>
    <link href="http://boinkor.net/archives/2012/08/git-dot-boinkor-dot-net-outage.html"/>
    <updated>2012-08-24T19:07:00-07:00</updated>
    <id>http://boinkor.net/archives/2012/08/git-dot-boinkor-dot-net-outage</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>I&#8217;m currently moving some of boinkor.net&#8217;s services off the creaky old machine that used to host it, over to another machine. This affects git.boinkor.net - it&#8217;s not going to be available for the next 2 days. (With a bit of luck, it may be back up a little sooner, though.)</p>

<p>This probably affects you if you follow the slime and mcclim git repos hosted there.</p>

<p>This was caused by a case of really bad planning on my part. Sorry for the inconvenience.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Converting the Mustache Test Suite to CL]]></title>
    <link href="http://boinkor.net/archives/2011/12/converting-the-mustache-test-suite-to-cl.html"/>
    <updated>2011-12-30T14:16:00-08:00</updated>
    <id>http://boinkor.net/archives/2011/12/converting-the-mustache-test-suite-to-cl</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>Matthew Snyder has a great <a href='http://msnyder.info/posts/2011/12/common-lisp-mustache/'>introductory post</a> on his blog where he converts the <a href='http://mustache.github.com/'>Mustache</a> <a href='https://github.com/mustache/spec'>spec</a> into a runnable <a href='http://common-lisp.net/project/bese/FiveAM.html'>fiveam</a> test suite. Very cool stuff - I hope he posts more (-:</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[IDNA Now Supports Punycode Decoding]]></title>
    <link href="http://boinkor.net/archives/2011/12/idna-now-supports-punycode-decoding.html"/>
    <updated>2011-12-21T19:27:00-08:00</updated>
    <id>http://boinkor.net/archives/2011/12/idna-now-supports-punycode-decoding</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>My <a href='http://github.com/antifuchs/idna'>IDNA library</a> now supports decoding IDNA strings via the to-unicode function:</p>

<pre><code>    (to-unicode &quot;xn--mller-kva.example.com&quot;)
    ;; =&gt;  &quot;müller.example.com&quot;</code></pre>

<p>That&#8217;s in addition to the regular encoding for unicode domain names:</p>

<pre><code>    (to-ascii &quot;müller.example.com&quot;)
    ;; =&gt; &quot;xn--mller-kva.example.com&quot;</code></pre>

<p>Sadly, I haven&#8217;t managed to get the logic for case-sensitive punycode encoding to work yet. But fortunately, IDNA domain name encoding doesn&#8217;t require that! Anyone looking for some low-hanging fruit-shaped lisp projects is welcome to add that! (-:</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Accessing the Stripe API From Lisp]]></title>
    <link href="http://boinkor.net/archives/2011/10/accessing-the-stripe-api-from-lisp.html"/>
    <updated>2011-10-08T23:31:00-07:00</updated>
    <id>http://boinkor.net/archives/2011/10/accessing-the-stripe-api-from-lisp</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p><a href='https://stripe.com'>Stripe</a> is a new payment processor on the Web, and they seem to be <a href='http://www.google.com/search?q=paypal+screwed+me'>a lot less insane</a> than Paypal. On a whim, I made a little (almost completely untested, toy) CL library for accessing their HTTP API from Lisp. Maybe you&#8217;ll find it useful: <a href='https://github.com/antifuchs/cl-stripe'>cl-stripe</a>.</p>

<p>This was pretty great fun! Thanks to their nice <a href='https://stripe.com/api/docs'>HTTP API</a>, <a href='http://weitz.de/drakma'>drakma</a>, and <a href='http://common-lisp.net/project/alexandria/'>alexandria</a>, I have been able to write this with a minimum of horribly hacky code, in just 5 or 6 hours of working on it, on and off, this saturday afternoon.</p>

<p>If it still looks like fun, I think I may add some clucumber tests to it tomorrow. Stay tuned.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[A Weird Problem With ASDF on NFS (and a workaround)]]></title>
    <link href="http://boinkor.net/archives/2011/10/a-weird-problem-with-asdf-on-nfs.html"/>
    <updated>2011-10-06T22:37:00-07:00</updated>
    <id>http://boinkor.net/archives/2011/10/a-weird-problem-with-asdf-on-nfs</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>Recently, we at Franz have been seeing weird failures when building a certain ASDF system on NFS: We were intermittently getting redefinition warnings when loading a system - not all the time, but more often when we compiled during certain timeslots.</p>

<p>This was a weird one, and I think it would be nice to record what this is, and how we figured out what&#8217;s going on (and how we arrived at our work-around).</p>

<p><strong>Update 2011-10-11</strong>: Faré <a href='http://lists.common-lisp.net/pipermail/asdf-devel/2011-October/002196.html'>informs</a> me that this problem is fixed (all the way, no need for a workaround) in ASDF 2.017.9!</p>

<h2 id='the_symptom'>The Symptom</h2>

<p>We have a system and a specialized operation (<code>concat-op</code>, it generates a single loadable .fasl we can ship) that depends on <code>asdf:load-op</code>. In our build process, we first load the system, then generate some data with the loaded system, and then perform the custom operation on the system.</p>

<p>When performing that operation with a source tree that was checked out on NFS, the <code>load-op</code> that it depends on sometimes got performed a second time: The lisp loaded all the .fasls again, and for some constructs in some .fasls, signaled a <code>WARNING</code>, which made the build break.</p>

<p>Oddly enough, the failure happened only during certain time slots - we would see the build work between 3pm and 4:30pm, and starting at 4:30 it failed consistently until it was time to go home. Huh.</p>

<h2 id='aside_how_asdf_operates'>Aside: How ASDF Operates</h2>

<p>Not everyone might be familiar with how ASDF works (if you are, feel free to skip to the next section, or stay and nitpick (-:), so here&#8217;s a small primer on what happens when you type <code>(asdf:load-system :some-system)</code>. Here&#8217;s a little walkthrough:</p>

<ol>
<li>
<p>ASDF runs the generic function <code>traverse</code> with the system and the operation as parameters.</p>
</li>

<li>
<p><code>traverse</code> walks the dependencies of the system and the contents of the system itself, and determines which operations are not yet done.</p>

<p>For a <code>load-op</code> on a CL source file, <code>traverse</code> will try to generate a <code>load-op</code> for the input-file of that load-op (the .fasl file), check if that .fasl file exists, and if it doesn&#8217;t, then it will also generate a <code>compile-op</code> for the corresponding .lisp file.</p>
</li>

<li>
<p>As a result, <code>traverse</code> returns a list of operations that must be performed on each component (or module, or system). For a clean source tree, that list looks something like: ((compile-op . source-1) (load-op . source-1) (compile-op . source-2) (load-op . source-2) &#8230;)</p>
</li>

<li>
<p><code>operate</code> takes that list and just performes each operation on its component in order.</p>
</li>
</ol>

<p>All this means that ASDF takes a two-step approach: It first determines what needs to be done, then does it. All the smarts in ASDF are in that <code>traverse</code> operation and the underlying mechanisms. The rest is just a dolist.</p>

<p>OK, with that out of the way:</p>

<h2 id='the_hunt'>The Hunt</h2>

<p>I&#8217;d gotten this error before, but that was when I was running on a source tree checked out on an NFS-mounted file system on Windows. I didn&#8217;t pay it much mind, because, hey, it&#8217;s the NFS client on Windows.</p>

<p>But then this exact same problem started happening to a client using two Linux machines as the client and the server. We had a problem.</p>

<p>At first, we suspected that there was an issue with build order (that result list of <code>traverse</code>). This was a blind alley: The files were loaded in exactly the same order in the failing and working scenarios. No luck.</p>

<p>The next thing was to instrument <code>operation-done-p</code> before performing the operation, and there we saw what happened: <code>operation-done-p</code> reported that <code>load-op</code> had not been performed on a file. But that file had been loaded into this very same image just minutes before! Huh?</p>

<p><code>operation-done-p</code> a generic function and has a method that attempts to handle the most common cases of operations on ASDF components: the method specialized on <code>(operation component)</code>, which does the following in the branch that applies to <code>load-op</code>:</p>
<figure class='code'><figcaption><span>(defmethod operation-done-p (operation component))</span></figcaption><div class='highlight'><table><tr><td class='gutter'><pre class='line-numbers'><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='cl'><span class='line'>  <span class='p'>(</span><span class='k'>let</span> <span class='p'>((</span><span class='nv'>out-files</span> <span class='p'>(</span><span class='nv'>output-files</span> <span class='nv'>o</span> <span class='nv'>c</span><span class='p'>))</span>
</span><span class='line'>        <span class='p'>(</span><span class='nv'>op-time</span> <span class='p'>(</span><span class='nv'>component-operation-time</span> <span class='nv'>o</span> <span class='nv'>c</span><span class='p'>)))</span>
</span><span class='line'>    <span class='p'>(</span><span class='k'>flet</span> <span class='p'>((</span><span class='nv'>latest-in</span> <span class='p'>()</span>
</span><span class='line'>             <span class='p'>(</span><span class='nb'>reduce</span> <span class='nf'>#&#39;</span><span class='nb'>max</span> <span class='p'>(</span><span class='nb'>mapcar</span> <span class='nf'>#&#39;</span><span class='nv'>safe-file-write-date</span> <span class='nv'>in-files</span><span class='p'>))))</span>
</span><span class='line'>      <span class='p'>(</span><span class='nb'>cond</span>
</span><span class='line'>        <span class='c1'>;; ...[cut some branches]</span>
</span><span class='line'>
</span><span class='line'>        <span class='p'>((</span><span class='nb'>not</span> <span class='nv'>out-files</span><span class='p'>)</span>
</span><span class='line'>         <span class='c1'>;; an operation without output-files is probably meant</span>
</span><span class='line'>         <span class='c1'>;; for its side-effects in the current image,</span>
</span><span class='line'>         <span class='c1'>;; assumed to be idem-potent,</span>
</span><span class='line'>         <span class='c1'>;; e.g. LOAD-OP or LOAD-SOURCE-OP of some CL-SOURCE-FILE.</span>
</span><span class='line'>         <span class='p'>(</span><span class='nb'>and</span> <span class='nv'>op-time</span> <span class='p'>(</span><span class='nb'>&gt;=</span> <span class='nv'>op-time</span> <span class='p'>(</span><span class='nv'>latest-in</span><span class='p'>))))</span>
</span><span class='line'>
</span><span class='line'>         <span class='c1'>;; ...[some more branches here]</span>
</span><span class='line'>         <span class='p'>)</span>
</span></code></pre></td></tr></table></div></figure>
<p>This consults a registry of times when an operation was performed on a component: <code>component-operation-time</code> returns a <a href='http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_u.htm#universal_time'>universal-time</a>, that is a number of seconds, and compares that to the <a href='http://www.lispworks.com/documentation/HyperSpec/Body/f_file_w.htm'>file-write-date</a> - also a universal-time - of the input file (the .fasl). After some tracing, we determined that for some reason, the .fasl file was one second younger than the time that ASDF thought the <code>load-op</code> had been performed on it. In other words, the compiler had written the file AFTER <a href='http://www.lispworks.com/documentation/HyperSpec/Body/f_load.htm'>load</a> had had a chance to read it. ASDF was reading a file from the future.</p>

<p>This was the time when we started scratching our heads.</p>

<p>First, we wrote a little test program to verify we weren&#8217;t crazy:</p>
<figure class='code'><figcaption><span>wtf.c</span></figcaption><div class='highlight'><table><tr><td class='gutter'><pre class='line-numbers'><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class='cp'>#include &lt;stdio.h&gt;</span>
</span><span class='line'><span class='cp'>#include &lt;sys/fcntl.h&gt;</span>
</span><span class='line'><span class='cp'>#include &lt;unistd.h&gt;</span>
</span><span class='line'><span class='cp'>#include &lt;stdlib.h&gt;</span>
</span><span class='line'><span class='cp'>#include &lt;sys/time.h&gt;</span>
</span><span class='line'><span class='cp'>#include &lt;sys/stat.h&gt;</span>
</span><span class='line'>
</span><span class='line'><span class='kt'>int</span> <span class='nf'>main</span><span class='p'>(</span><span class='kt'>int</span> <span class='n'>argc</span><span class='p'>,</span> <span class='kt'>char</span> <span class='o'>**</span><span class='n'>argv</span><span class='p'>)</span> <span class='p'>{</span>
</span><span class='line'>  <span class='kt'>int</span> <span class='n'>fd</span><span class='p'>;</span>
</span><span class='line'>  <span class='k'>struct</span> <span class='n'>timeval</span> <span class='n'>tv1</span><span class='p'>,</span> <span class='n'>tv2</span><span class='p'>;</span>
</span><span class='line'>  <span class='k'>struct</span> <span class='n'>stat</span> <span class='n'>sb1</span><span class='p'>,</span> <span class='n'>sb2</span><span class='p'>;</span>
</span><span class='line'>
</span><span class='line'>  <span class='kt'>char</span> <span class='n'>buf</span><span class='p'>[</span><span class='mi'>1024</span><span class='p'>];</span>
</span><span class='line'>
</span><span class='line'>  <span class='k'>while</span> <span class='p'>(</span><span class='mi'>1</span><span class='p'>)</span> <span class='p'>{</span>
</span><span class='line'>    <span class='n'>gettimeofday</span><span class='p'>(</span><span class='o'>&amp;</span><span class='n'>tv1</span><span class='p'>,</span> <span class='nb'>NULL</span><span class='p'>);</span>
</span><span class='line'>    <span class='n'>printf</span><span class='p'>(</span><span class='s'>&quot;%u.%u</span><span class='se'>\n</span><span class='s'>&quot;</span><span class='p'>,</span> <span class='n'>tv1</span><span class='p'>.</span><span class='n'>tv_sec</span><span class='p'>,</span> <span class='n'>tv1</span><span class='p'>.</span><span class='n'>tv_usec</span><span class='p'>);</span>
</span><span class='line'>
</span><span class='line'>    <span class='n'>fd</span><span class='o'>=</span><span class='n'>open</span><span class='p'>(</span><span class='n'>argv</span><span class='p'>[</span><span class='mi'>1</span><span class='p'>],</span> <span class='n'>O_WRONLY</span><span class='o'>|</span><span class='n'>O_CREAT</span><span class='o'>|</span><span class='n'>O_TRUNC</span><span class='p'>,</span> <span class='mo'>0660</span><span class='p'>);</span>
</span><span class='line'>
</span><span class='line'>    <span class='n'>write</span><span class='p'>(</span><span class='n'>fd</span><span class='p'>,</span> <span class='n'>buf</span><span class='p'>,</span> <span class='k'>sizeof</span><span class='p'>(</span><span class='n'>buf</span><span class='p'>));</span>
</span><span class='line'>    <span class='n'>write</span><span class='p'>(</span><span class='n'>fd</span><span class='p'>,</span> <span class='n'>buf</span><span class='p'>,</span> <span class='k'>sizeof</span><span class='p'>(</span><span class='n'>buf</span><span class='p'>));</span>
</span><span class='line'>    <span class='n'>write</span><span class='p'>(</span><span class='n'>fd</span><span class='p'>,</span> <span class='n'>buf</span><span class='p'>,</span> <span class='k'>sizeof</span><span class='p'>(</span><span class='n'>buf</span><span class='p'>));</span>
</span><span class='line'>
</span><span class='line'>    <span class='n'>gettimeofday</span><span class='p'>(</span><span class='o'>&amp;</span><span class='n'>tv1</span><span class='p'>,</span> <span class='nb'>NULL</span><span class='p'>);</span>
</span><span class='line'>    <span class='n'>stat</span><span class='p'>(</span><span class='n'>argv</span><span class='p'>[</span><span class='mi'>1</span><span class='p'>],</span> <span class='o'>&amp;</span><span class='n'>sb1</span><span class='p'>);</span>
</span><span class='line'>    <span class='n'>close</span><span class='p'>(</span><span class='n'>fd</span><span class='p'>);</span>
</span><span class='line'>    <span class='n'>gettimeofday</span><span class='p'>(</span><span class='o'>&amp;</span><span class='n'>tv2</span><span class='p'>,</span> <span class='nb'>NULL</span><span class='p'>);</span>
</span><span class='line'>    <span class='n'>stat</span><span class='p'>(</span><span class='n'>argv</span><span class='p'>[</span><span class='mi'>1</span><span class='p'>],</span> <span class='o'>&amp;</span><span class='n'>sb2</span><span class='p'>);</span>
</span><span class='line'>
</span><span class='line'>    <span class='cm'>/* never seems to be triggered */</span>
</span><span class='line'>    <span class='k'>if</span> <span class='p'>(</span><span class='n'>sb2</span><span class='p'>.</span><span class='n'>st_mtime</span> <span class='o'>!=</span> <span class='n'>sb1</span><span class='p'>.</span><span class='n'>st_mtime</span><span class='p'>)</span> <span class='p'>{</span>
</span><span class='line'>      <span class='n'>printf</span><span class='p'>(</span><span class='s'>&quot;mtime changed between last write (%d) and close (%u)</span><span class='se'>\n</span><span class='s'>&quot;</span><span class='p'>,</span>
</span><span class='line'>	     <span class='n'>sb1</span><span class='p'>.</span><span class='n'>st_mtime</span><span class='p'>,</span> <span class='n'>sb2</span><span class='p'>.</span><span class='n'>st_mtime</span><span class='p'>);</span>
</span><span class='line'>      <span class='n'>exit</span><span class='p'>(</span><span class='mi'>1</span><span class='p'>);</span>
</span><span class='line'>    <span class='p'>}</span>
</span><span class='line'>    <span class='k'>if</span> <span class='p'>(</span><span class='n'>sb1</span><span class='p'>.</span><span class='n'>st_mtime</span> <span class='o'>&gt;</span> <span class='n'>tv1</span><span class='p'>.</span><span class='n'>tv_sec</span><span class='p'>)</span> <span class='p'>{</span>
</span><span class='line'>      <span class='n'>printf</span><span class='p'>(</span><span class='s'>&quot;mtime after last write has a future timestamp (%u &gt; %u)</span><span class='se'>\n</span><span class='s'>&quot;</span><span class='p'>,</span>
</span><span class='line'>	     <span class='n'>sb1</span><span class='p'>.</span><span class='n'>st_mtime</span><span class='p'>,</span> <span class='n'>tv1</span><span class='p'>.</span><span class='n'>tv_sec</span><span class='p'>);</span>
</span><span class='line'>      <span class='n'>exit</span><span class='p'>(</span><span class='mi'>1</span><span class='p'>);</span>
</span><span class='line'>    <span class='p'>}</span>
</span><span class='line'>    <span class='k'>if</span> <span class='p'>(</span><span class='n'>sb2</span><span class='p'>.</span><span class='n'>st_mtime</span> <span class='o'>&gt;</span> <span class='n'>tv2</span><span class='p'>.</span><span class='n'>tv_sec</span><span class='p'>)</span> <span class='p'>{</span>
</span><span class='line'>      <span class='n'>printf</span><span class='p'>(</span><span class='s'>&quot;mtime after close has a future timestamp (%u &gt; %u)</span><span class='se'>\n</span><span class='s'>&quot;</span><span class='p'>,</span>
</span><span class='line'>	     <span class='n'>sb2</span><span class='p'>.</span><span class='n'>st_mtime</span><span class='p'>,</span> <span class='n'>tv2</span><span class='p'>.</span><span class='n'>tv_sec</span><span class='p'>);</span>
</span><span class='line'>      <span class='n'>exit</span><span class='p'>(</span><span class='mi'>1</span><span class='p'>);</span>
</span><span class='line'>    <span class='p'>}</span>
</span><span class='line'>  <span class='p'>}</span>
</span><span class='line'>
</span><span class='line'>  <span class='k'>return</span> <span class='mi'>0</span><span class='p'>;</span>
</span><span class='line'><span class='p'>}</span>
</span></code></pre></td></tr></table></div></figure>
<p>When we ran it, after a while, at timestamps very close to the boundary to the next second, we&#8217;d get &#8220;mtime after close has a future timestamp&#8221;. <a href='http://www.osnews.com/story/19266/WTFs_m'>What. The.</a></p>

<p>We checked that all machines were synchronized with NTP. They were, to the same machine on the local network. What is going on?</p>

<p>Luckily, my colleague Ahmon has a lot of experience with <a href='http://nfsforwindows.com/home'>NFS</a>. His expertise and ample use of tcpdump finally provided the final puzzle piece: NFS protocol 3 on Linux has a feature called &#8220;weak cache consistency&#8221;: information can be supplied by servers after most NFS calls (e.g., WRITE) and has the server&#8217;s take on file attributes (such as mtime). So if the time on the server is just a tiny bit ahead of the client, the server will report the file that the client just wrote is from the client&#8217;s future.</p>

<p>When one apparently time-traveling file appears in the source tree, the <code>traverse</code> method will consider the system to not have been loaded, and will reload the .fasl files starting at the time-traveling file. Anything after that file in the build order could (and did!) potentially mess up the lisp image. In the best case, it would just slow down the build a lot by re-loading a ton of .fasl files. Argh.</p>

<h2 id='fixing_this_mess_aka_the_workaround'>Fixing this Mess (aka, the Workaround)</h2>

<p>Since ASDF consults a registry of times that a file was loaded, we decided it would be easiest to alter the method that records this timestamp: Instead of the current time, it should record whichever is later: the current time or the timestamp of the file that it loaded.</p>
<figure class='code'><figcaption><span>ASDF work-around for NFS files created in the future</span></figcaption><div class='highlight'><table><tr><td class='gutter'><pre class='line-numbers'><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='cl'><span class='line'><span class='p'>(</span><span class='nb'>defmethod</span> <span class='nv'>perform</span> <span class='ss'>:after</span> <span class='p'>((</span><span class='nv'>operation</span> <span class='nv'>load-op</span><span class='p'>)</span> <span class='p'>(</span><span class='nv'>c</span> <span class='nv'>component</span><span class='p'>))</span>
</span><span class='line'>  <span class='p'>(</span><span class='nb'>setf</span> <span class='p'>(</span><span class='nb'>gethash</span> <span class='p'>(</span><span class='nb'>type-of</span> <span class='nv'>operation</span><span class='p'>)</span> <span class='p'>(</span><span class='nv'>component-operation-times</span> <span class='nv'>c</span><span class='p'>))</span>
</span><span class='line'>    <span class='p'>(</span><span class='nb'>reduce</span> <span class='nf'>#&#39;</span><span class='nb'>max</span> <span class='p'>(</span><span class='nb'>cons</span> <span class='p'>(</span><span class='nb'>get-universal-time</span><span class='p'>)</span>
</span><span class='line'>                        <span class='p'>(</span><span class='nb'>mapcar</span> <span class='nf'>#&#39;</span><span class='nv'>safe-file-write-date</span> <span class='p'>(</span><span class='nv'>input-files</span> <span class='nv'>operation</span> <span class='nv'>c</span><span class='p'>))))))</span>
</span></code></pre></td></tr></table></div></figure>
<p>And that&#8217;s it - with this method in place, asdf can now accurately build our system repeatedly, on NFS, even if wtf.c triggers.</p>

<h2 id='lessons_learned'>Lessons Learned</h2>

<p>That was a pretty fun afternoon spent debugging our build process. As a result, we got a working build, and a few shiny new ideas in our heads:</p>

<p>One, a program should never rely on the system time and some file&#8217;s creation time being comparable. This just doesn&#8217;t work anymore in a distributed system, especially if you&#8217;re using full seconds to represent time.</p>

<p>Two, ASDF is pretty flexible (almost to the point of being too flexible). To diagnose ASDF&#8217;s internal state, all we had to do was <a href='http://www.lispworks.com/documentation/HyperSpec/Body/m_tracec.htm'>trace</a> some functions it defines, and we managed to put this workaround in without having to deeply modify any of its sources: All it takes is an additional :after method. Sweet.</p>

<p>And three, the Allegro CL fasl loader is very fast (at least it feels so to me, coming from SBCL): In that tiny window (less than 0.07 seconds of real time) it would load a pretty substantial .fasl file and asdf would register it as loaded. That&#8217;s pretty impressive! (-:</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[New Lisp Tips Blogs]]></title>
    <link href="http://boinkor.net/archives/2011/09/new-lisp-tips-blogs.html"/>
    <updated>2011-09-30T10:14:00-07:00</updated>
    <id>http://boinkor.net/archives/2011/09/new-lisp-tips-blogs</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>As of today, there are two new Lisp tips blogs on the web: <a href='http://lisptips.tumblr.com'>Common Lisp tips</a> by <a href='http://xach.livejournal.com/'>Xach</a> and <a href='http://slime-tips.tumblr.com/'>SLIME tips</a> by Stas. Both already have some nice stuff that I didn&#8217;t know about, so I hope they keep the tips coming!</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA["Basic Lisp Techniques" for sale on Amazon]]></title>
    <link href="http://boinkor.net/archives/2011/09/basic-lisp-techniques-for-sale-on-amazon.html"/>
    <updated>2011-09-27T14:00:00-07:00</updated>
    <id>http://boinkor.net/archives/2011/09/basic-lisp-techniques-for-sale-on-amazon</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>I just saw that David Cooper&#8217;s <a href='http://www.franz.com/resources/educational_resources/cooper.book.pdf'>Basic Lisp Techniques</a> is now available <a href='http://www.amazon.com/Basic-Lisp-Techniques-ebook/dp/B0058V1FJG/ref=sr_1_1'>on Amazon, as a Kindle edition</a>, for $9.95.</p>

<p>Problem is, this book is freely available <a href='http://www.franz.com/resources/educational_resources/cooper.book.pdf'>on the Franz web site</a>, doesn&#8217;t seem like it is an authorized conversion, and judging from the free sample the Kindle edition it is a slightly crappy (footnotes didn&#8217;t convert properly, ToC is ugly) pdf-&gt;azw conversion of the PDF. I recommend using the free PDF (you can even convert it yourself using <a href='http://calibre-ebook.com/'>Calibre</a> if you want the book on an e-reader, it&#8217;s not hard at all to get very readable results).</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Switching this blog to Octopress]]></title>
    <link href="http://boinkor.net/archives/2011/09/switching_this_blog_to_octopress.html"/>
    <updated>2011-09-26T22:56:00-07:00</updated>
    <id>http://boinkor.net/archives/2011/09/switching_this_blog_to_octopress</id>
    
    <content type="html"><![CDATA[<p>I&#8217;ve just made the jump from a creaky, old, badly-styled and annoying Movable Type installation to <a href='http://octopress.org'>Octopress</a>. It looks pretty nice, and as of now works better than MT has ever worked here, although I hope I didn&#8217;t break too many things with the switch over. If you find any 404s where they shouldn&#8217;t be, please contact me.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[SBCL git repo is now official!]]></title>
    <link href="http://boinkor.net/archives/2011/06/sbcl_git_repo_is_now_official.html"/>
    <updated>2011-06-07T06:16:02-07:00</updated>
    <id>http://boinkor.net/archives/2011/06/sbcl_git_repo_is_now_official</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>Almost exactly four years after I <a href='http://comments.gmane.org/gmane.lisp.steel-bank.devel/7033'>announced</a> the availability of the first iteration of the SBCL cvs-&gt;git mirror (and a few more years after the <a href='http://permalink.gmane.org/gmane.lisp.steel-bank.devel/2536'>cvs-&gt;arch mirror even</a>!), we now have an official SBCL repository! Many thanks to Nikodemus for doing all the heavy lifting! (-:</p>

<p>I&#8217;ve stopped the (now defuct) cvs-&gt;git mirror. In its place, there is a new repository that mirrors the official git repo. To get the old, now static repo, see <a href='http://git.boinkor.net/gitweb/sbcl-cvs-import.git'>sbcl-cvs-import</a>.</p>

<p>If you are a committer and have any old checkouts around, please make sure that you follow <a href='http://thread.gmane.org/gmane.lisp.steel-bank.devel/15983/focus=15987'>the procedure for eliminating unnecessary branches / tags</a>.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Userscript for nicer l1sp.org search]]></title>
    <link href="http://boinkor.net/archives/2010/11/userscript_for_nicer_l1sporg_s.html"/>
    <updated>2010-11-20T08:28:00-08:00</updated>
    <id>http://boinkor.net/archives/2010/11/userscript_for_nicer_l1sporg_s</id>
    <category term="Hacks" /><category term="Lisp" />
    <content type="html"><![CDATA[<p>I love the <a href='http://l1sp.org'>l1sp.org</a> documentation redirection service. It is quick and easy to look stuff up there if you know the name, and it has a pretty good search if you don&#8217;t. However, the search results are not presented very nicely: They&#8217;re very close together, and the ones I&#8217;m looking for most often (mostly CLHS pages) are buried somewhere in the middle.</p>

<p>So I wrote a userscript (for Firefox through greasemonkey or Google Chrome/Chromium) to improve things a bit: It enables keyboard navigation (j/k or cursor-down/up select the next/previous result, enter opens the page, and / focuses the input field), and searches for the closest match from the results (ranks them by section and then selects the shortest matching entry).</p>

<p><a href='https://github.com/antifuchs/userscripts/raw/master/l1sp_org-cursor-support.user.js'>Get the l1sp.org userscript here.</a></p>

<p>I do hope that Xach will consider adding these features to l1sp proper (maybe de-uglified a bit&#8230; that yellow highlight is really not too great). Until then, you can already enjoy the benefits of just a little javascript (-:</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Visualizing the CLHS interlinking structure]]></title>
    <link href="http://boinkor.net/archives/2010/09/visualizing_the_clhs_interlink.html"/>
    <updated>2010-09-25T00:52:02-07:00</updated>
    <id>http://boinkor.net/archives/2010/09/visualizing_the_clhs_interlink</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>Lately, I&#8217;ve been wondering what we could use the ~110k Hyperlinks in the CLHS for, other than click through the spec in a web browser. For example, given a glossary entry, how do you find out which functions refer to it?</p>

<p>So I wrote a little program that crawls (a local copy of) the HyperSpec, and creates an RDF graph of its link structure. This graph can be used to answer these questions, or it could be the basis of a useful research overlay on top of the HyperSpec, who knows.</p>

<p>Lots of tools exist to visualize and manipulate the facts in an RDF graph already; Here&#8217;s a screen shot of <a href='http://www.franz.com/agraph/gruff/'>gruff</a> with a small trace through the HyperSpec:</p>
<img src='http://boinkor.net/assets/images/gruff-with-hyperspec-rdf.png' title='Gruff with Hyperspec RDF' />
<p>You can get the code to create the RDF graph <a href='http://github.com/antifuchs/clsem/'>at github</a>.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Huge life changes ahead.]]></title>
    <link href="http://boinkor.net/archives/2010/08/huge_life_changes_ahead.html"/>
    <updated>2010-08-11T00:09:39-07:00</updated>
    <id>http://boinkor.net/archives/2010/08/huge_life_changes_ahead</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>In October, I will be moving to California to work for a <a href='http://franz.com/'>hugely awesome lisp company</a> there.</p>

<p>Exciting and excellent times ahead, I&#8217;m sure!</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[I think this is going to be pretty sweet.]]></title>
    <link href="http://boinkor.net/archives/2010/04/i_think_this_is_going_to_be_pr.html"/>
    <updated>2010-04-28T19:51:47-07:00</updated>
    <id>http://boinkor.net/archives/2010/04/i_think_this_is_going_to_be_pr</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<img src='http://boinkor.net/assets/images/clucumber-baby-steps.png' title='Clucumber baby steps' />
<p>I&#8217;ve been toying around with making a Common Lisp adapter to <a href='http://cukes.info/'>Cucumber</a>, a behavior-driven development tool. I think this will really be very sweet.</p>

<p>Here&#8217;s how the step definition file would look like (these are really just stubs; in reality, you&#8217;d put in the lisp code you want to happen for the given textual description):</p>
<figure class='code'><figcaption><span>Clucumber example steps  </span></figcaption>
 <div class='highlight'><table><tr><td class='gutter'><pre class='line-numbers'><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='cl'><span class='line'><span class='p'>(</span><span class='nv'>Given*</span> <span class='err'>#</span><span class='nv'>?</span><span class='s'>&quot;^I start clucumber in (.*)$&quot;</span> <span class='p'>(</span><span class='nv'>path</span><span class='p'>)</span>
</span><span class='line'>  <span class='p'>(</span><span class='nb'>assert</span> <span class='nv'>path</span><span class='p'>))</span>
</span><span class='line'>
</span><span class='line'><span class='p'>(</span><span class='nv'>When*</span> <span class='err'>#</span><span class='nv'>?</span><span class='s'>&quot;^I define some-other-package as the test package$&quot;</span> <span class='p'>()</span>
</span><span class='line'>  <span class='p'>(</span><span class='nv'>pending</span><span class='p'>))</span>
</span><span class='line'>
</span><span class='line'><span class='p'>(</span><span class='nv'>Then*</span> <span class='err'>#</span><span class='nv'>?</span><span class='s'>&quot;^the current package should be \&quot;([^\&quot;]+)\&quot;$&quot;</span> <span class='p'>(</span><span class='nb'>package-name</span><span class='p'>)</span>
</span><span class='line'>  <span class='p'>(</span><span class='nv'>pending</span> <span class='p'>(</span><span class='nb'>format</span> <span class='no'>nil</span> <span class='s'>&quot;package is ~A&quot;</span> <span class='nb'>package-name</span><span class='p'>)))</span>
</span></code></pre></td></tr></table></div></figure>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Vienna informal Lisp meeting 2010-02-27]]></title>
    <link href="http://boinkor.net/archives/2010/02/vienna_informal_lisp_meeting_2.html"/>
    <updated>2010-02-19T10:02:49-08:00</updated>
    <id>http://boinkor.net/archives/2010/02/vienna_informal_lisp_meeting_2</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>I&#8217;m pleased to announce the first international totally informal lisp meeting in Vienna. A bunch of Lisp hackers are coming to here on the 27th, and we will meet, talk and hack in and around the Metalab.</p>

<p>The approximate schedule is as follows:</p>

<p><em>12:30</em> - Lunch at <a href='http://www.frommehelene.at/'>Fromme Helene</a>. There is a table reserved for 15 persons (we&#8217;re 5 confirmed hackers at the moment). If you want in, drop me a line.<br />~<em>14:30</em> - Reconvene at the Metalab for hacking/coffee/cold drinks/(optional) lightning talks</p>

<p>Hackers who have confirmed they&#8217;ll be there, so far:</p>

<ul>
<li>Tobias C. Rittweiler</li>

<li>Luke Gorrie</li>

<li>Stelian Ionescu</li>

<li>Attila Lendvai</li>

<li>Gábor Melis</li>

<li>Mark Evenson</li>
</ul>

<p>Thanks to the SBCL team for infusing this event with positive energy. SBCL: Harming the software industry since 1999.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[cl-beanstalk: A queue service client]]></title>
    <link href="http://boinkor.net/archives/2010/01/cl-beanstalk_a_queue_service_c.html"/>
    <updated>2010-01-31T17:49:52-08:00</updated>
    <id>http://boinkor.net/archives/2010/01/cl-beanstalk_a_queue_service_c</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>Over the weekend, I wrote a little client library to a queue server that I&#8217;ve grown very fond of over the last year, <a href='http://kr.github.com/beanstalkd/'>beanstalk</a>. It&#8217;s a very simple queue server, but it comes with a nice feature (delayed jobs) that I&#8217;ve had a use for recently.</p>

<p>The queue server is nicely engineered (written in C, works with queues a few million jobs deep), and very fast; it has guards in the protocol against worker failure, and it was a pleasure to implement: The whole thing is just 320 lines of code, including comments.</p>

<p>You can get the source (and a tiny example) at the <a href='http://github.com/antifuchs/cl-beanstalk'>cl-beanstalk</a> github repository.</p>

<p>Hope this is useful for anyone else - I am planning on using this in autobench myself, to distribute work across several build hosts.</p>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Hunchentoot gets a debugging-acceptor]]></title>
    <link href="http://boinkor.net/archives/2009/11/hunchentoot_gets_a_debugging-a.html"/>
    <updated>2009-11-15T22:11:36-08:00</updated>
    <id>http://boinkor.net/archives/2009/11/hunchentoot_gets_a_debugging-a</id>
    <category term="Lisp" />
    <content type="html"><![CDATA[<p>Today, I submitted a patch (the first free software lisp one in months for me!) to the <a href='http://weitz.de/hunchentoot/'>Hunchentoot</a> project, and it got accepted. Yay!</p>

<p>Some backstory: Hunchentoot&#8217;s 1.0.0 release dropped a lot of implementation-dependent features, among them functionality to invoke the debugger if an error happens while handling a request. While <a href='http://paste.lisp.org/display/81046'>workarounds</a> <a href='http://www.lispworks.com/documentation/HyperSpec/Body/v_break_.htm'>exist</a>, none of them were obvious to new users or users who recently upgraded.</p>

<p>The patch I sent should fix this, hopefully. It adds a rudimentary error handling protocol to Hunchentoot, and provides two generic functions whose behavior can be adapted to your error handling needs. You can see for yourself in Hunchentoot&#8217;s <a href='http://bknr.net/trac/browser/trunk/thirdparty/hunchentoot'>svn repository</a>.</p>

<p>If you&#8217;re a Hunchentoot user, I urge you to test this (in both development mode using debuggable-acceptor and running with the default settings). The sooner you find bugs, the sooner they can be fixed, the sooner a release can be pushed out. And if you don&#8217;t find bugs at all, that&#8217;s cool, too (-:</p>]]></content>
  </entry>
  
</feed>
