<?xml version="1.0" encoding="UTF-8"?>

<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>Technomancy</title>
  <id>tag:technomancy.us,2007:blog/</id>
  <link href="http://technomancy.us/feed/atom" rel="self" type="application/atom+xml"/>
  <link href="http://technomancy.us/" rel="alternate" type="text/html"/>
  <updated>2013-05-17T00:08:22Z</updated>

  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20a%20turtle%20moves%20things%20forward</id>
    <published>2013-05-15T18:09:44Z</published>
    <updated>2013-05-15T18:09:44Z</updated>

    <link href="http://technomancy.us/167" rel="alternate" type="text/html"/>
    <title>in which a turtle moves things forward</title>
    <content type="html">
      &lt;p&gt;Recently on the Clojure mailing list someone
started &lt;a href=&quot;https://groups.google.com/group/clojure/browse_thread/thread/e6feafe15b0908d4&quot;&gt;an
interesting thread on what motivates you as a programmer&lt;/a&gt;. My
friend &lt;a href=&quot;http://nelsonmorris.net/&quot;&gt;Nelson Morris&lt;/a&gt;
responded as so:&lt;/p&gt;

&lt;blockquote&gt;Contributions and projects start off well, and energy might
  wane depending on time and life factors.  Even contributing to
  tools used by many of the members of the community like [Leiningen] and
  Clojars doesn't prevent it. What helps is direct involvement by
  someone else.&lt;/blockquote&gt;

&lt;p&gt;This really resonated with me because it emphasizes that people are
  more important than programs. For me sharing is the thing that
  makes programming even worth doing in the first place. So it got
  me thinking about different technologies and what kind of people
  they're good for helping.&lt;/p&gt;

&lt;p&gt;If you follow my writing it will be obvious that I enjoy working
  in Emacs and Clojure. While these are among the most powerful,
  flexible technologies I know of, collaborating with others on
  tools for Emacs and Clojure basically limits me to working with
  professional programmers, because both environments are very poor
  from a beginner's perspective. If I'm working solo or on a team of
  seasoned hackers, I'll definitely be most effective with
  Clojure. If my primary goal is to interact with the widest group
  of programmers possible, I would use Ruby as it's the most
  commonly-used language I can bring myself to
  use. But if I want to reach out to people who
  don't already spend all day thinking about functions and data
  structures, well that's another thing entirely.&lt;/p&gt;

&lt;p&gt;This is particularly relevant for me personally as a father. I'm
  taking an active role in the education of my sons, and of course I
  think technical literacy must be an important part of it. But when you
  look at how computers used in traditional educational settings,
  you're much more likely to see computers programming children than
  children programming computers. So I've been looking for ways to
  foster technical skills and encourage algorithmic thinking in
  engaging ways that can keep the attention of my five-year-old.&lt;/p&gt;

&lt;img src=&quot;/i/scratch.jpg&quot; alt=&quot;scratch&quot; align=&quot;left&quot; style=&quot;margin-left: 0&quot; /&gt;

&lt;p&gt;In the
  book &lt;a href=&quot;http://www.amazon.com/Mindstorms-Children-Computers-Powerful-Ideas/dp/0465046746&quot;&gt;Mindstorms&lt;/a&gt;,
  Seymour Papert describes the shift from concrete reasoning to
  formal reasoning as one of the main transitions children undergo
  as they learn to think like adults. One of the design goals of the
  Logo system he created was to provide transitional concepts to
  bridge the gap between the two.&lt;/p&gt;

&lt;p&gt;Children interact with Logo by giving commands to an onscreen
  object known as
  the &lt;a href=&quot;https://en.wikipedia.org/wiki/Turtle_graphics&quot;&gt;turtle&lt;/a&gt;.
  While the turtle lives in the abstract world of geometry comprised
  of points and lines, children are able to identify with it since
  they tell it to move in ways which they can relate to&amp;mdash;it has
  a heading and position, and it turns and moves forward and
  backwards just like they do. Because the turtle's movements on the
  screen are isomorphic to their own physical movements, it gives
  them a model to help them grasp abstract geometrical concepts
  though they're only used to thinking in concrete terms. And
  &lt;a href=&quot;https://en.wikipedia.org/wiki/Jean_Piaget#Education:_Teaching_and_Learning&quot;&gt;Piagetian
  learning&lt;/a&gt;&amp;mdash;ambient, natural learning which children are so
  adept at doing without study&amp;mdash;is all a matter of building
  models of the world.&lt;/p&gt;

&lt;p&gt;Most people know Logo from its original triangle-turtle-centric
  incarnation, but in Mindstorms Papert describes Logo as more of an
  educational philosophy than any single program, language, or
  implementation. A more recent version of Logo
  is &lt;a href=&quot;http://scratch.mit.edu/&quot;&gt;Scratch&lt;/a&gt;, a drag-and-drop
  visual programming environment from MIT's Media Lab targeting
  school children. Since my older son is an early reader he's been
  able to construct simple scripts (with some guidance) for the
  characters within Scratch, watching them interact with each other
  and even in some
  cases &lt;a href=&quot;https://www.youtube.com/watch?v=vVRIryCOA50&quot;&gt;the
  outside world&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While it's been lots of fun to come up with ideas and talk
  through how we'd bring them to life on the screen, one of the most
  rewarding parts is watching his problem-solving abilities
  develop. Papert talks about how children are often afraid to try
  things for fear of failure, but Scratch teaches that debugging is
  a normal part of making things work. Rather than &quot;does it work&quot;,
  the question becomes &quot;how can we make it work?&quot; This was
  demonstrated the other day (outside the context of Scratch) when
  he was putting together
  some &lt;a href=&quot;http://snapcircuits.net/&quot;&gt;Snap Circuits&lt;/a&gt;:&lt;/p&gt;

  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; 
          src=&quot;http://www.youtube-nocookie.com/embed/hBsegnCBhtw?rel=0&quot; 
          frameborder=&quot;0&quot; allowfullscreen&gt;&lt;/iframe&gt;

&lt;p&gt;Of course the goal is not to produce &quot;little programmers&quot;. It's
  primarily about developing the ability to think systematically,
  but it extends beyond that into getting them thinking about
  thinking itself. In some sense once they're in the habit of asking
  the right epistemological questions, the parent or teacher almost
  just needs to get out of the way and let them explore. At that
  point the process of discovering a topic &lt;i&gt;with&lt;/i&gt; someone is
  much more rewarding than telling facts &lt;i&gt;at&lt;/i&gt; them.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20everything%20is%20ephemeral</id>
    <published>2013-04-12T08:56:45Z</published>
    <updated>2013-04-12T08:56:45Z</updated>

    <link href="http://technomancy.us/166" rel="alternate" type="text/html"/>
    <title>in which everything is ephemeral</title>
    <content type="html">
      &lt;p&gt;There's been enough written about the benefits
of &lt;a href=&quot;http://ted.io/celebrate-remote-work.html&quot;&gt;remote&lt;/a&gt; 
  &lt;a href=&quot;http://sysadvent.blogspot.com/2012/12/day-15-remote-working-right-way.html&quot;&gt;work&lt;/a&gt;
  that I'm not sure I can add much to it beyond anecdotes. From my
  own experience I've been a remote worker for all but a year and a
  half of my career and have loved it. The amount of time wasted by
  cars commuting is sobering, and the ability to start the day after
  simply crossing my back yard to the
  &lt;a href=&quot;https://secure.flickr.com/photos/technomancy/tags/laboratory&quot;&gt;code
  lab&lt;/a&gt; is not something I'd give up lightly. Especially with
  warmer weather coming up in Seattle the draw
  of &lt;a href=&quot;https://secure.flickr.com/photos/technomancy/tags/remoteoffice/&quot;&gt;working
  outdoors&lt;/a&gt; and at &lt;a href=&quot;/156&quot;&gt;various coffee shops&lt;/a&gt; is
  strong indeed.&lt;/p&gt;

&lt;img src=&quot;/i/syme.png&quot; align=&quot;left&quot; alt=&quot;syme splash&quot;
     style=&quot;margin-left: 0;&quot; /&gt;

&lt;p&gt;But the thing about remote work is that it can be really
  difficult to do effectively. At my last job we were dedicated from
  the outset to making the fully-remote model work, and we were able
  to assemble a team that functioned fantastically well while
  drawing from talent all over the country. But in order to make
  this work we had to set things up so that no one operated in
  isolation. We had our daily stand-ups, but more important was
  spending the bulk of the time paired with another hacker over SSH
  and VoIP. And even when not paired, there was the understanding
  that you could easily grab someone to get a real-time review of
  whatever you were writing.&lt;/p&gt;

&lt;p&gt;In order to facilitate this, we would usually set up a shared
  user on each laptop (or sometimes on an unused server sitting
  under a desk somewhere) and do the necessary port forwarding
  wrangling and public key management to ensure others could SSH in
  and join our &lt;tt&gt;tmux&lt;/tt&gt; sessions. Given that it was something
  we relied on every day it wasn't particularly onerous to set things
  up, and over time the tools got a bit better.
  (&lt;a href=&quot;http://vagrantup.com&quot;&gt;Vagrant&lt;/a&gt; to manage pairing VMs,
  a common repository for the team's pubkeys, etc.)&lt;/p&gt;

&lt;p&gt;These days things are different&amp;mdash;I'm at a company that
  embraces a remote/local mix of teams rather than being fully
  remote. While I've got co-workers who are happy to discuss and
  review code remotely, I can't assume everyone has spent the time
  to to facilitate remote collaboration if it's not an everyday tool
  for them. And when you're looking for another set of eyes on a
  problem, you need frictionless tools; otherwise you might not even
  bother asking for help. So I put
  together &lt;a href=&quot;https://syme.herokuapp.com&quot;&gt;Syme&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Syme sets up disposable EC2 hosts for collaborating on GitHub
  projects via &lt;tt&gt;ssh&lt;/tt&gt; and &lt;tt&gt;tmux&lt;/tt&gt;. The idea came from a
  fantastic site called &lt;a href=&quot;https://pair.io&quot;&gt;pair.io&lt;/a&gt;, which
  has since unfortunately fallen into disrepair. (There's a great
  video on their splash page explaining things if you've got a
  couple minutes.) Basically you give it the name of a project you
  want to hack on and who you want to hack on it with, and it can
  preconfigure the host by checking out a copy, adding SSH public keys
  for all invited users, and running all the necessary setup scripts
  to get dependencies and user settings installed. Then everyone
  just SSHes into the machine and joins a shared tmux session, and
  it's all yours.&lt;/p&gt;

&lt;p&gt;I had access to the private alpha of pair.io, but since
  billing hadn't been implemented yet I always felt a bit guilty
  whenever I launched a machine to work on since it would just rack
  up the hours in the author's Amazon account.&lt;/p&gt;

&lt;p&gt;I'd been thinking what it would take to implement that kind of
  thing myself but had been dissuaded by the idea of writing a
  billing system. Whenever you're dealing with money on behalf of
  the user it can hardly be considered a for-fun project. But then I
  realized that can be neatly sidestepped simply by prompting for
  the user's AWS credentials while launching the instances. It turns
  out keeping those around in an encrypted cookie in the browser
  makes it possible to perform further operations on the user's
  behalf without getting into the harrowing business of storing
  secrets. It also means it can be done completely as free software,
  and it's not tied to myself at all&amp;mdash;if I lose interest and
  wander off anyone else can pick it up and deploy on their own.&lt;/p&gt;

&lt;p&gt;So I've gotten it to the point where I'm pretty happy with it. At
  just a shade over 500 lines of Clojure it's quite tidy. I'm hoping
  it comes in handy streamlining things at work, but it's open for
  any remote collaborators who may find it useful in any kind
  of pairing contexts. If you run into any issues trying it out or
  have suggestions, please head over to
  the &lt;a href=&quot;https://github.com/technomancy/syme/issues/new&quot;&gt;GitHub
  issue tracker&lt;/a&gt; and let me know.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20we%20cater%20to%20those%20with%20an%20allergic%20reaction%20to%20parentheses</id>
    <published>2013-04-01T20:42:59Z</published>
    <updated>2013-04-01T20:42:59Z</updated>

    <link href="http://technomancy.us/165" rel="alternate" type="text/html"/>
    <title>in which we cater to those with an allergic reaction to parentheses</title>
    <content type="html">
      &lt;p&gt;&lt;a href=&quot;https://groups.google.com/group/clojure/browse_thread/thread/1d97dff96dbc5430&quot;&gt;A&lt;/a&gt; &lt;a href=&quot;https://gist.github.com/headius/5285216&quot;&gt;lot&lt;/a&gt;
  of &lt;a href=&quot;http://www.smbc-comics.com/?id=2491&quot;&gt;people&lt;/a&gt; have
  been talking about how parentheses are such a big barrier to
  adoption of Clojure these days. Apparently they're pretty
  intimidating when you're used to a language with a lot of curly
  braces and things. While I can't do anything about Clojure itself,
  I realized we could make some changes to Leiningen that would
  allow newcomers to return to the comfort of XML. My latest plugin
  is
  called &lt;a href=&quot;https://github.com/technomancy/lein-xml&quot;&gt;lein-xml&lt;/a&gt;,
  and it lets you write this:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;nxml-processing-instruction-delimiter&quot;&gt;&amp;lt;?&lt;/span&gt;&lt;span class=&quot;nxml-processing-instruction-target&quot;&gt;xml&lt;/span&gt; &lt;span class=&quot;nxml-attribute-local-name&quot;&gt;version&lt;/span&gt;=&lt;span class=&quot;nxml-attribute-value-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nxml-attribute-value&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;nxml-attribute-value-delimiter&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nxml-attribute-local-name&quot;&gt;encoding&lt;/span&gt;=&lt;span class=&quot;nxml-attribute-value-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nxml-attribute-value&quot;&gt;UTF-8&lt;/span&gt;&lt;span class=&quot;nxml-attribute-value-delimiter&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nxml-processing-instruction-delimiter&quot;&gt;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;org.leiningen&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;artifactId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;sample&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;artifactId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;0.1.0-SNAPSHOT&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;Just some kind of sample thing&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;https://github.com/technomancy/lein-xml&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;licenses&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;license&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;Eclipse Public License&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;license&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;licenses&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;org.clojure&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;artifactId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;clojure&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;artifactId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;1.5.1&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;slamhound&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;groupId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;artifactId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;slamhound&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;artifactId&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nxml-text&quot;&gt;1.3.3&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nxml-tag-slash&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;nxml-element-local-name&quot;&gt;project&lt;/span&gt;&lt;span class=&quot;nxml-tag-delimiter&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;...instead of this bewildering, unfamiliar invocation:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;esk-paren&quot;&gt;&lt;span class=&quot;hl-line&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;&lt;span class=&quot;hl-line&quot;&gt;defproject&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hl-line&quot;&gt; &lt;/span&gt;&lt;span class=&quot;function-name&quot;&gt;&lt;span class=&quot;hl-line&quot;&gt;org.leiningen/sample&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hl-line&quot;&gt; &lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&lt;span class=&quot;hl-line&quot;&gt;&quot;0.1.0-SNAPSHOT&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hl-line&quot;&gt;
&lt;/span&gt;  &lt;span class=&quot;constant&quot;&gt;:description&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;Just some kind of sample thing&quot;&lt;/span&gt;
  &lt;span class=&quot;constant&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;https://github.com/technomancy/lein-xml&quot;&lt;/span&gt;
  &lt;span class=&quot;constant&quot;&gt;:license&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;Eclipse Public License&quot;&lt;/span&gt;
            &lt;span class=&quot;constant&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&lt;/span&gt;}
  &lt;span class=&quot;constant&quot;&gt;:dependencies&lt;/span&gt; [[org.clojure/clojure &lt;span class=&quot;string&quot;&gt;&quot;1.5.1&quot;&lt;/span&gt;]
                 [slamhound &lt;span class=&quot;string&quot;&gt;&quot;1.3.3&quot;&lt;/span&gt;]]&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;I know it probably won't be used by anyone who has spent much
  time with Clojure, but for newcomers hopefully this will remove
  one of the big blockers for enterprise developers trying out
  Clojure.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20laziness%20mostly%20predominates</id>
    <published>2012-09-26T15:41:26Z</published>
    <updated>2012-09-26T15:41:26Z</updated>

    <link href="http://technomancy.us/164" rel="alternate" type="text/html"/>
    <title>in which laziness mostly predominates</title>
    <content type="html">
      &lt;p&gt;The last few days have been a whirlwind with the
  fantastic &lt;a href=&quot;https://thestrangeloop.com/&quot;&gt;StrangeLoop&lt;/a&gt;
  conference, such that I forgot to mention that my interview with
  Chas Emerick of the Mostly Lazy podcast
  was &lt;a href=&quot;http://mostlylazy.com/2012/09/21/episode-8-phil-hagelberg-empowering-userspace-in-heroku-leiningen-and-emacs/&quot;&gt;posted
  recently&lt;/a&gt;. In it we rant about user-serviceable hardware, my
  recent reading list, extensible systems, the latest developments
  in &lt;a href=&quot;http://leiningen.org&quot;&gt;Leiningen&lt;/a&gt;, and more.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20we%20retire%20a%20workhorse</id>
    <published>2012-08-22T05:50:47Z</published>
    <updated>2012-08-22T05:50:47Z</updated>

    <link href="http://technomancy.us/163" rel="alternate" type="text/html"/>
    <title>in which we retire a workhorse</title>
    <content type="html">
      &lt;p&gt;When I &lt;a href=&quot;/121&quot;&gt;first got started with Clojure&lt;/a&gt; I was
  disappointed that the process of getting started was pretty rough
  around the edges, but one of the things that helped sustain my
  momentum was the fact that I could
  use &lt;a href=&quot;http://common-lisp.net/project/slime/&quot;&gt;SLIME&lt;/a&gt; to
  write Clojure code from Emacs via
  the &lt;a href=&quot;https://github.com/technomancy/swank-clojure&quot;&gt;swank-clojure&lt;/a&gt;
  project, running and testing it while in the middle of writing it.
  This helped me overlook the fact that I needed to construct
  nasty &lt;tt&gt;java&lt;/tt&gt; command-line invocations to launch the
  process.&lt;/p&gt;

&lt;img src=&quot;/i/uw.jpg&quot; alt=&quot;UW&quot; align=&quot;right&quot; /&gt;

&lt;p&gt;As time went on I did what I could to try to improve the
  situation. &lt;a href=&quot;https://github.com/technomancy/clojure-mode/blob/47caba15ff31f339e74378fd3c05bcffa7091550/clojure-mode.el#L627&quot;&gt;Early&lt;/a&gt; &lt;a href=&quot;https://github.com/technomancy/emacs-starter-kit/blob/63797f61aa7019e2cc5f0e79793f8bdd4b50ad21/starter-kit-lisp.el#L39&quot;&gt;attempts&lt;/a&gt;
  were quite primitive, but eventually
  when &lt;a href=&quot;http://leiningen.org&quot;&gt;Leiningen&lt;/a&gt; came out
  the &lt;tt&gt;lein swank&lt;/tt&gt; command obsoleted all my hokey elisp
  setup scripting. Still a few other things kept it from being a
  really smooth experience. The main issue is that SLIME was
  developed primarily for Common Lisp. The protocol behind it
  changes every so often, and there aren't any stable releases;
  users are expected to simply run straight out of CVS.&lt;/p&gt;

&lt;p&gt;When the creator of swank-clojure passed maintainership to me, I
  kept things going by applying patches and adding a handful of
  features at the edges, but nobody really understood the ins and
  outs of the project. Part of this was because it was just a really
  old quirky codebase, (most of it predated the introduction of
  Clojure atoms) but part of it was because it was a fairly literal
  port of the Common Lisp server.&lt;/p&gt;

&lt;p&gt;The end result was that SLIME moved forward while swank-clojure
  stood still. This mostly worked once we bundled a frozen SLIME
  revision, but it was common to have confused users wander into the
  IRC channel with a broken setup, unsure of where it went wrong or
  how to get the right SLIME. Even for experienced users it was
  impossible to have a setup that could connect to both Clojure and
  Common Lisp at the same time.&lt;/p&gt;

&lt;p&gt;Meanwhile in Clojure-land
  the &lt;a href=&quot;https://github.com/clojure/tools.nrepl&quot;&gt;nREPL&lt;/a&gt;
  project was started as a tool-agnostic counterpart to
  swank-clojure, building a general networked repl server and an
  ecosystem around it. While I appreciated the idea, I thought that
  it would be a long time before Emacs support for it could catch up
  to the level of functionality in SLIME.&lt;/p&gt;

&lt;p&gt;Still, one evening on a short flight to San Francisco I bashed
  out the beginnings of an nREPL client in Emacs. I got a bit stuck
  on the socket-based bencode functionality and dropped it after the
  flight, but not before pushing the code out and
  &lt;a href=&quot;http://groups.google.com/group/clojure/browse_thread/thread/2bd91de7dca55ca4&quot;&gt;mentioning
    it on the Clojure mailing list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately Tim
  King &lt;a href=&quot;https://github.com/kingtim/nrepl.el/&quot;&gt;picked it back
  up&lt;/a&gt;, and now it has quickly become a respectable competitor to
  SLIME. I've switched over to it for day-to-day use. The main thing
  I've noticed about it is how accessible the codebase is; I've
  found it very easy to dive in and add features. So even though
  it's still missing a few things that SLIME boasts, it's on course
  to improve at a steady pace. It also works out of the box with
  Leiningen 2.x's &lt;tt&gt;repl&lt;/tt&gt; task without any extra plugin
  needed.&lt;/p&gt;

&lt;p&gt;It's now gotten to the point where I'm ready to consider
  swank-clojure deprecated. Of course, it will go on working, so if
  you already have a setup that works for you, there's no need to
  switch. Also if you find yourself particularly attached to the
  inspector or debugger from SLIME you might want to hold off. (For
  debugging in particular
  the &lt;a href=&quot;https://github.com/pallet/ritz&quot;&gt;ritz&lt;/a&gt; project
  works with SLIME and has advanced debugging features.) But if
  you're looking for a simpler way to get
  started, &lt;a href=&quot;https://github.com/kingtim/nrepl.el&quot;&gt;give
  nrepl.el a try&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: Jeffrey Chu, original author of swank-clojure,
  writes: &lt;i&gt;As creator of swank-clojure, I'm a little sad to see it
  die. But I'm very happy to see that it's being replaced by
  something designed/maintainable instead of hacked about just to
  scratch an itch. Thanks for keeping it alive all this time and I
  look forward to the great work coming out of nrepl.el.&lt;/i&gt;&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20the%20facts%20are%20laid%20out%20concerning%20swarm%20coding</id>
    <published>2012-07-01T04:43:18Z</published>
    <updated>2012-07-01T04:43:18Z</updated>

    <link href="http://technomancy.us/162" rel="alternate" type="text/html"/>
    <title>in which the facts are laid out concerning swarm coding</title>
    <content type="html">
      &lt;p&gt;I've found that user groups often fall into a pattern of lecture
  style presentations with slide shows. Since it's usually difficult
  to find presenters, often it ends up that after a while whoever
  founded the group speaks repeatedly. This leads to burn-out and
  isn't sustainable even if you're fortunate enough to have
  presenters who are skilled public speakers. It's also simply not a
  very good way to learn; your mind is a lot more involved in a when
  engaged in active discussion.&lt;/p&gt;

&lt;p&gt;This is why at the &lt;a href=&quot;http://seajure.github.com&quot;&gt;Seattle
  Clojure group&lt;/a&gt; we follow a different model that focuses on code
  and participation. A few months ago at
  the &lt;a href=&quot;http://clojurewest.org&quot;&gt;ClojureWest conference&lt;/a&gt; I
  gave
  a &lt;a href=&quot;https://github.com/strangeloop/clojurewest2012-slides/raw/master/Hagelberg-SwarmCoding.pdf&quot;&gt;short
  talk&lt;/a&gt; (PDF) explaining the motivation behind this style. I call
  it &quot;swarm coding&quot;.&lt;/p&gt;

&lt;img src=&quot;i/athens.jpg&quot; alt=&quot;school at athens&quot; align=&quot;right&quot; /&gt;

&lt;p&gt;The Socratic Method is a form of learning that centers around
  getting people to ask the right questions rather than just
  telling. It's often used in group settings with classical
  education methods, and I've found it's a great way to run a user
  group meeting as well. If you can get everyone hooked in to
  participate in a shared editor session and come up with an idea
  for a small project, you can collaborate in a unique, engaging way
  and learn a lot.&lt;/p&gt;

&lt;p&gt;We've found SSH, tmux, and Emacs to be a great combination for
  this. The host prepares his machine with a new user created just
  for the purpose of swarming, and everyone is given the username,
  hostname, and password to log in over SSH.&lt;/p&gt;

&lt;p&gt;
  &lt;kbd&gt;$ ssh swarm@zuse.local&lt;/kbd&gt;&lt;br /&gt;
  &lt;kbd&gt;$ tmux attach&lt;/kbd&gt;
&lt;/p&gt;

&lt;p&gt;Once logged in, running &lt;kbd&gt;tmux attach&lt;/kbd&gt; allows a user to
  join an in-process session started by the host, and control can be
  passed around as discussion progresses. If someone has an idea for
  how to address a certain problem, they can just try it out
  straight away. While there are more complicated setups that can
  allow for each user to edit independently, we've found that is
  usually not what you want. If you have a discussion going on, you
  want a single point to focus on. It's really hard to track what's
  happening if you have multiple independent edits happening
  simultaneously.&lt;/p&gt;

&lt;p&gt;Usually skill levels vary widely in group settings like this, so
  it's important for the facilitator to be able to gauge them,
  usually by just getting quick introductions from everyone in the
  group beforehand. The temptation is often for those that really
  know their way around to just power through and write some
  slick code, perhaps pausing to explain a particularly subtle
  technique. It's more rewarding to let control pass around the
  group and try to keep everyone involved, but it can be
  difficult.&lt;/p&gt;

&lt;p&gt;It's probably a good idea to dedicate a meeting to a tooling
  workshop at first to get people started. Especially with Clojure
  the initial setup can be intimidating, so the newcomers find
  it valuable to get help just getting the basics working on their
  laptops. While I don't recommend newcomers try to learn Emacs as
  their main editor at the same time as picking up a new
  language, it really makes collaboration over SSH much easier, so
  basic familiarity is helpful.&lt;/p&gt;

&lt;p&gt;I've coded
  up &lt;a href=&quot;https://github.com/technomancy/swarming&quot;&gt;some
  scripts&lt;/a&gt; to automate setup of swarming sessions. It handles
  getting basic dotfiles in place and provides instructions for how
  to join a session when people log in. Right now it only supports
  Clojure, Leiningen, and Emacs, but there's no reason it couldn't
  be extended for Vim or other languages.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: I
  have &lt;a href=&quot;https://syme.herokuapp.com&quot;&gt;another project called
    Syme&lt;/a&gt; which handles setting up pair/swarm nodes on EC2. In
  most cases this is a lot simpler than running the tmux session on
  your own machine, though it may not always be feasible depending
  on the quality of the network.&lt;/p&gt;

&lt;p&gt;There are a few things to watch for here. First of all, we've
  only tried this with groups of up to 12 participants. Group
  dynamics break down when things get larger, so you may want to
  split up into groups. You could try splitting a project into
  independent parts that could be coded by each group if your
  project divides naturally this way, or you could try both tackling
  the same problem and comparing solutions at the end.&lt;/p&gt;

&lt;p&gt;Picking a project to try is also tricky. You want it to be
  somewhat useful and not contrived, but you also need it to match
  the skill level of the group and still be able to make progress on
  it in a couple hours. It's a lot of fun if you end up with a
  project you can publish to Clojars or Heroku at the end.&lt;/p&gt;

&lt;p&gt;Happy Hacking!&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20three%20programming%20methods%20are%20compared</id>
    <published>2012-05-16T16:07:43Z</published>
    <updated>2012-05-16T16:07:43Z</updated>

    <link href="http://technomancy.us/161" rel="alternate" type="text/html"/>
    <title>in which three programming methods are compared</title>
    <content type="html">
      &lt;p&gt;There are, roughly speaking, three ways to develop large
  user-facing programs, which we will refer to here as 0) the Unix
  way, 1) the Emacs way, and 2) the wrong way.&lt;/p&gt;

&lt;p&gt;The Unix way has been expounded upon at length many times. It
  consists of many small programs which communicate by sending text
  over pipes or using the occasional signal. If you can get away
  with using this model, the simplicity and universality it offers
  is very compelling. You hook into a rich ecosystem of text-based
  processes with a long history of well-understood conventions.
  Anyone can tie into it with programs written in any language. But
  it's not well-suited for everything: sometimes the requirement of
  keeping each part of the system in its own process is too high a
  price to pay, and sometimes circumstances require a richer
  communication channel than just a stream of text.&lt;/p&gt;

&lt;p&gt;This is where the Emacs way shines. A small core written in a
  low-level language implements a higher-level language in which
  most of the rest of the program is implemented. Not only does the
  higher-level language ease the development of the trickier parts
  of the program, but it also makes it much easier to implement a
  good extension system since extensions are placed on even ground
  with the original program itself. I wrote about this in an earlier
  post on the live-development model Emacs offers:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;If you have to use some tacked-on &quot;plugin mechanism&quot;
  to customize it, then you’re going to be limited at the very least
  by the imagination of the author of the plugin mechanism; only the
  things he thought you would want to do with it are doable. But if
  you’re using the exact same tools as the original authors were
  using to write the program in the first place, you can bet they
  put all their effort into making that a seamless, powerful
  experience, and you'll be able to access things on an entirely new
  level.&lt;/p&gt;
  &lt;p&gt;-&lt;a href=&quot;/115&quot;&gt;in which a subject is attempted to be
      approached objectively, though such a thing is actually
      impossible&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;It's worth noting that this is the model under which Mozilla is
  developed. The core Mozilla platform is implemented mostly in a
  gnarly mash of C++, but applications
  like Firefox and &lt;a href=&quot;http://conkeror.org&quot;&gt;Conkeror&lt;/a&gt;
  are primarily written in JavaScript, as are extensions. Following
  the Emacs way accounted for Firefox's continuing popularity even
  back when it was getting trounced by competitors in terms of
  JavaScript performance. Chrome's extension mechanism is laughably
  simplistic in comparison.&lt;/p&gt;

&lt;p&gt;Finally for completeness sake, the wrong way is simply to write a
  large monolithic application in a low-level language, usually C++.
  Often half-hearted attempts at extension mechanisms are bolted on
  to programs developed this way, (usually in order to check off
  another box on a features list) but they are invariably
  frustrating and primitive and don't end up offering extension
  developers the same access to program internals that the
  developers of the original program itself have.&lt;/p&gt;

&lt;p&gt;The Unix way makes particularly explicit the notion of composing
  small programs, but the Emacs way shines when a single runtime
  process plays host to a number of independent programs that can
  interact with each other gracefully. For instance,
  the &lt;a href=&quot;&quot;&gt;Magit&lt;/a&gt; version control interface can run in the
  same Emacs instance as a SLIME session controlling a lisp project.
  They coexist in a complimentary way and compose together without
  interference. So rather than saying there are three ways to write
  large user-facing programs, it might be more accurate to say that
  there are zero good ways to write large user-facing programs and
  two ways to compose a number of small programs into a coherent
  system.&lt;/p&gt;

&lt;p&gt;This is especially interesting to me right now since it has come
  to my attention that when it was rewritten in the transition from
  version 2 to version
  3, &lt;a href=&quot;http://blog.ometer.com/2008/08/25/embeddable-languages/&quot;&gt;GNOME
  has switched to the second way&lt;/a&gt; via an embedded JavaScript
  runtime, which means things are about to
  get &lt;a href=&quot;https://github.com/technomancy/lein-gnome&quot;&gt;very
  interesting&lt;/a&gt;.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20korean%20hardware%20pleases</id>
    <published>2012-05-09T04:25:14Z</published>
    <updated>2012-05-09T04:25:14Z</updated>

    <link href="http://technomancy.us/160" rel="alternate" type="text/html"/>
    <title>in which korean hardware pleases</title>
    <content type="html">
      &lt;p&gt;I just got a new laptop, and while I wouldn't post about it
  normally I've had a number of people ask me what I thought of it.
  For the last five years or so I've been a Thinkpad user
  exclusively, but the latest models of their lightweight X series
  have offered disappointing screens with 1366x786 being the only
  resolution option. Since my dignity prevents me from buying a
  laptop with fewer vertical pixels than my phone, I broadened my
  search this time around and came across the 13-inch Samsung Series
  9, aka NP900X3B.&lt;/p&gt;

&lt;img src=&quot;/i/zuse.jpg&quot; alt=&quot;zuse&quot; align=&quot;right&quot; /&gt;

&lt;p&gt;&lt;b&gt;Update August 2012:&lt;/b&gt; I've gone back to the old Thinkpad after
  getting it repaired. I realized after a month or so with the
  Samsung that the ultra-thin profile is actually an anti-feature. I
  like my laptops small, but I realized that weight is really the
  only thing I care about in that category. There are no concrete
  benefits to be had from a thin design, while fact that it forces a
  shallow, uncomfortable keyboard, inaccessible battery, less-sturdy
  chassis, and hard-wired SSD are very tangible drawbacks. I
  definitely do miss the brilliant screen, but I'm glad to be on a
  machine that doesn't sacrifice functionality for looks. If you're
  considering a Series 9, I'd still say it's better than most
  laptops on the market. If you're coming from a Macbook, the
  keyboard and inaccessible battery might not bother you, but I'd
  encourage you to take a look at the Thinkpad X series too.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;At first the most striking thing about it is its thin profile,
  but once you've started using it you realize it's the screen that
  really sets it apart. Samsung is the largest manufacturer of LCDs
  in the world, so it makes sense that the screen would be the
  distinguishing factor on their high-end models. The resolution is
  1600x900, which I couldn't find in anything else smaller than 14
  inches, but it's the 400 cd/m&lt;sup&gt;2&lt;/sup&gt; of brightness that
  really sets it apart. With summer right around the corner, this
  perfect
  for &lt;a href=&quot;http://www.flickr.com/photos/technomancy/tags/remoteoffice/&quot;&gt;working
  outdoors&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While I'm pretty thrilled with the new machine, the thin profile
  forces some difficult compromises with the keyboard. It's of the
  standard chicklet design, which is a drag coming from the
  Thinkpad. The X200s Thinkpad I was using previously had a very
  comfortable response and depth to it that you don't find with
  most chicklet keyboards. The Samsung also lacks a trackpoint, drainage
  tray, and dedicated volume buttons. The X200s isn't appreciably
  heavier than the Samsung; (only ½ a pound more) but it is over
  twice as thick. Personally if it's light enough I don't find
  thickness to be a problem; I'd much rather have a comfortable
  keyboard than a 0.4-inch profile. But it does turn heads.&lt;/p&gt;

&lt;p&gt;The speakers on ultraportable laptops like the X200s typically
  come across as a token effort to fill a feature checkbox, but the
  Samsung's are fairly respectable. I was pleased to see the lack of
  the hardware wifi kill switch that plagued me on my Thinkpad. It's
  also got a bigger selection of ports than you'd expect with the
  slim profile: USB 2, USB 3, micro-HDMI, combined mic/headphones,
  and Ethernet, though the latter must be used with the provided
  adapter.&lt;/p&gt;

&lt;p&gt;I'm running Debian Wheezy on it because I
  read &lt;a href=&quot;http://www.ing.unitn.it/~zatelli/linux/samsung.html&quot;&gt;on
  a blog post&lt;/a&gt; that a newer kernel was needed for the wifi
  drivers, but apparently this was only the case with an earlier
  model of the Series 9 that used the Broadcom chipset; The NP900X3B
  uses an Intel chipset that has been well-supported for some time.
  Everything worked perfectly out of the box except the keys for
  adjusting the keyboard backlight. The camera, external monitor
  port, and multitouch trackpad work as you'd expect. The suspend
  functionality, which on Linux has traditionally lagged behind Mac
  OS X, resumes in a couple short seconds.&lt;/p&gt;

&lt;p&gt;While I would love to see this same screen on a laptop with the
  luxurious keyboard, carbon fiber body, and replaceable battery of a
  Thinkpad, the Series 9 is quite slick and should keep me happy for a
  number of years.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20we%20plot%20an%20escape%20from%20the%20quagmire%20of%20equality</id>
    <published>2012-04-27T05:45:01Z</published>
    <updated>2012-04-27T05:45:01Z</updated>

    <link href="http://technomancy.us/159" rel="alternate" type="text/html"/>
    <title>in which we plot an escape from the quagmire of equality</title>
    <content type="html">
      &lt;p&gt;If you follow happenings in the Clojure world, you may have
  noticed that the announcement
  of &lt;a href=&quot;https://github.com/halgari/clojure-py&quot;&gt;clojure-py&lt;/a&gt;
  brings the number of runtimes targeted by Clojure up to four.
  While it's great to see the language expand beyond the JVM, it's
  not too exciting to me personally since the runtime I spend the
  most time in by far is that of Emacs. Of course, Emacs can already
  be programmed with lisp, but the dialect it uses leaves much to be
  desired. I miss first-class functions[&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;],
  destructuring, literals for associative data, and immutability
  whenever I find myself writing nontrivial Emacs Lisp code, and the
  lack of these features makes me reluctant to write in it even
  though it offers an environment with live feedback unmatched by anything
  but
  the &lt;a href=&quot;http://en.wikipedia.org/wiki/Smalltalk#Image-based_persistence&quot;&gt;Smalltalk
  images&lt;/a&gt;
  and &lt;a href=&quot;http://en.wikipedia.org/wiki/Genera_(operating_system)&quot;&gt;Lisp
  Machines&lt;/a&gt; of yore.&lt;/p&gt;

&lt;img src=&quot;http://p.hagelb.org/duck-hand.png&quot; align=&quot;right&quot;
     alt=&quot;why? why do you need a reason?&quot; 
     title=&quot;why? why do you need a reason?&quot; /&gt;

&lt;p&gt;Part of what makes ClojureScript interesting is that it has
  distilled the number of primitives needed to port Clojure to a new
  runtime down to a small number due to its push towards
  self-hosting. One of the Summer of Code projects for this year is
  to allow for pluggable backends in the ClojureScript compiler,
  with Lua as a proof of concept. I started to think through what
  would be necessary for this to happen on the Emacs runtime, but I
  ran into an interesting quandary
  regarding &lt;a href=&quot;http://en.wikipedia.org/wiki/Referential_transparency_(computer_science)&quot;&gt;referential
  transparency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A function is referentially transparent when it is guaranteed to
  return the same value every time it is called with a given set of
  arguments. This is a great guarantee to be able to make about your
  functions as it reduces the number of things you need to keep
  in your head. Referentially transparent functions really can be
  thought of as black boxes: always deterministic and predictable.
  But a function that operates on mutable objects cannot be
  referentially transparent&amp;mdash;it cannot make any guarantees that
  future calls involving that same object will result in the same
  value since that object could have a different value at any time.&lt;/p&gt;

&lt;p&gt;Since Emacs Lisp does not provide any immutable data types other
  than numbers, only functions that operate on numbers alone can be
  referentially transparent. This is a shame, since referential
  transparency allows you to have much greater confidence that your
  code is correct. Without it, the best you can say is &quot;as long as
  the rest of this program behaves itself, this function should
  work&quot;. This works a lot like older OSes with cooperative
  multitasking and no process memory isolation, which is to say not
  very well.&lt;/p&gt;

&lt;p&gt;But arguably the worst thing about lacking referential
  transparency is that you can't check for equality in a
  meaningful way. Henry Baker's
  paper &lt;a href=&quot;http://home.pipeline.com/~hbaker1/ObjectIdentity.html&quot;&gt;&quot;Equal
  Rights for Functional Objects&quot;&lt;/a&gt; addresses the problem of
  equality in Common Lisp[&lt;a href=&quot;#fn2&quot;&gt;2&lt;/a&gt;], which is notable for
  having a plethora of equality predicates depending on what level
  of coarseness you want:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;EQ&lt;/code&gt;: are both arguments the same object in memory?&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;EQL&lt;/code&gt;: like &lt;code&gt;EQ&lt;/code&gt;, but compares value for
    non-immediate numbers like tagged fixnums and bignums.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;EQUAL&lt;/code&gt;: do both arguments have the same structure
    and contents?&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;EQUALP&lt;/code&gt;: like &lt;code&gt;EQUAL&lt;/code&gt;, but allows
    integers to equal floats and ignores string case.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;=&lt;/code&gt;: are both numbers of equal value?&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;CHAR=&lt;/code&gt;, &lt;code&gt;STRING=&lt;/code&gt;, &lt;code&gt;STRING-EQUAL&lt;/code&gt;,
    ad nauseum.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Baker argues that this confusion is due to a muddled notion of
  object identity. Everything would be so much simpler if we could
  think in terms of operational equivalence:&lt;/p&gt;

&lt;blockquote&gt;Two objects are &quot;operationally equivalent&quot; if and only
  if there is no way that they can be distinguished, using ...
  primitives other than [equality primitives].[RNRS]&lt;/blockquote&gt;

&lt;p&gt;Asking &quot;Are these two objects stored in the same memory
  location?&quot; lets implementation details leak into your code.
  Equality should really be about &quot;Can these two objects behave
  differently in any observable way?&quot;. Baker's paper implements the
  &lt;code&gt;egal&lt;/code&gt; function in terms of this question; it's an
  equality predicate that defines object identity as a transitive
  closure of immutable attributes of an object. So without
  immutability, &lt;code&gt;egal&lt;/code&gt; can't do any better than &lt;code&gt;eq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, with a compiler that targets Emacs Lisp, you can work
  around this by implementing your own immutable types. But this
  makes any form of interop much more cumbersome; close integration
  with its host platform is one of the core design tenets of
  Clojure, and having to perform conversions on basic types like
  strings just to call native elisp functions would be too
  cumbersome to be practical. So with the Emacs runtime as it is,
  you have to choose between getting equality right and seamless
  interop. Damned if you do; damned if you don't.&lt;/p&gt;

&lt;p&gt;There are two possible solutions to this. There has been an
  ongoing effort to
  get &lt;a href=&quot;http://www.emacswiki.org/emacs/GuileEmacs&quot;&gt;Emacs Lisp
  running on the Guile VM&lt;/a&gt;. As it comes from a Scheme background,
  Guile provides the immutable data types we need. (&lt;b&gt;Update&lt;/b&gt;:
  the immutable types offered in Guile do not actually come from the
  Scheme standard, but they are present nonetheless.) However, Emacs
  Lisp is very different from Scheme, and the Guile implementors
  have an enormous task ahead of them to get even the basics
  working; the amount of work needed to achieve compatibility with
  the bulk of quirky extant Emacs Lisp code is daunting. But it
  would be wonderful if it were possible.&lt;/p&gt;

&lt;p&gt;The more realistic option is to add immutable data types to the
  existing Emacs implementation. I suggested in passing on the
  emacs-devel mailing list that I would be interested in financially
  supporting such a feature, which led
  to &lt;a href=&quot;http://lists.gnu.org/archive/html/emacs-devel/2012-04/msg00684.html&quot;&gt;
  a discussion of its pros and cons&lt;/a&gt;. It turns out there is very
  little code that would break if Emacs strings became immutable;
  even though it's possible to change strings it happens rarely
  enough in real code to not pose serious problems. Changing lists
  to be immutable would break huge amounts of code though, so a more
  reasonable approach would be to offer an alternate list
  constructor for immutable lists so that functional code could have
  access to immutability without wreaking havoc on existing
  functionality. Ideally libraries could opt-in to having the reader
  return immutable lists, but I don't know enough about Emacs's
  reader to know how feasible this is.&lt;/p&gt;

&lt;p&gt;Unfortunately I'm not the one calling the shots; I don't have the
  C skills necessary to make it happen even if the Emacs maintainers
  were favourable to the idea. But it's interesting to think about,
  especially as concurrency may be introduced in Emacs 25. One
  certainly can dream.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;[&lt;a name=&quot;fn1&quot;&gt;1&lt;/a&gt;] Common Lisp fans claim that lisp-2s have
  first-class functions, but the way they are kept separate from
  other first-class values in their own namespace ghetto brings to
  mind claims of &quot;separate but equal&quot;&amp;mdash;at best it is Jim Crow
  functional programming.&lt;/p&gt;

&lt;p&gt;[&lt;a name=&quot;fn2&quot;&gt;2&lt;/a&gt;] Emacs Lisp is not Common Lisp, but the two
  dialects are very closely related, and for the purposes of this
  post share the same problems.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20we%20coin%20a%20term%20which%20is%20the%20opposite%20of%20deprecate</id>
    <published>2012-02-29T00:57:50Z</published>
    <updated>2012-02-29T00:57:50Z</updated>

    <link href="http://technomancy.us/158" rel="alternate" type="text/html"/>
    <title>in which we coin a term which is the opposite of deprecate</title>
    <content type="html">
      &lt;p&gt;Earlier this month
  I &lt;a href=&quot;http://lein-survey.herokuapp.com/results&quot;&gt;published the
  results&lt;/a&gt; of a survey I had posted for Leiningen users. I got a
  lot of great data, but I especially appreciated the free-form
  &quot;other comments&quot; section that let people just ramble. I was happy
  to see that many of the suggestions have already been implemented
  in the ongoing work on Leiningen 2.&lt;/p&gt;

&lt;blockquote&gt;I still think lein-multi should be rolled into core so
  people can be encouraged to test cross-version.&lt;/blockquote&gt;

&lt;h4&gt;Profiles&lt;/h4&gt;

&lt;p&gt;This brings me to what is probably the biggest feature in
  Leiningen 2.0: profiles. In Leiningen 1 we had some special
  cases to segregate out &quot;dev&quot; mode from the rest so you would only
  have certain dependencies or directories available during
  development. This is useful to have, but the implementation was
  pretty ad-hoc and riddled with special cases. During
  some &lt;a href=&quot;/155&quot;&gt;discussion with the Cake developers&lt;/a&gt; we
  talked about whether that could be generalized, which turned into
  the idea of profiles.&lt;/p&gt;

&lt;p&gt;So now rather than a handful of project configuration keys that
  are only active during development time, we have a &lt;tt&gt;:dev&lt;/tt&gt;
  profile that's active by default where you can keep your
  additional test-only &lt;tt&gt;:dependencies&lt;/tt&gt;
  and &lt;tt&gt;:resource-paths&lt;/tt&gt;. You can also keep all
  your &lt;tt&gt;:plugins&lt;/tt&gt; that you want active across all projects in
  the &lt;tt&gt;:user&lt;/tt&gt; profile rather than using &lt;kbd&gt;lein plugin
  install&lt;/kbd&gt;. That way there is only ever one plugin list active
  at a time, meaning it can be de-duplicated, avoiding messy conflicts.&lt;/p&gt;

&lt;p&gt;But you can also create profiles for other situations:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;defproject clj-http &lt;span class=&quot;string&quot;&gt;&quot;0.3.3-SNAPSHOT&quot;&lt;/span&gt;
  &lt;span class=&quot;constant&quot;&gt;:description&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;A Clojure HTTP library&quot;&lt;/span&gt;
  &lt;span class=&quot;constant&quot;&gt;:url&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;https://github.com/dakrone/clj-http/&quot;&lt;/span&gt;
  &lt;span class=&quot;constant&quot;&gt;:min-lein-version&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;2.0.0&quot;&lt;/span&gt;
  &lt;span class=&quot;constant&quot;&gt;:dependencies&lt;/span&gt; [[org.clojure/clojure &lt;span class=&quot;string&quot;&gt;&quot;1.3.0&quot;&lt;/span&gt;] &lt;span class=&quot;comment-delimiter&quot;&gt;; &lt;/span&gt;&lt;span class=&quot;comment&quot;&gt;elided below...
&lt;/span&gt;                 [org.apache.httpcomponents/httpclient &lt;span class=&quot;string&quot;&gt;&quot;4.1.2&quot;&lt;/span&gt;]
                 [cheshire &lt;span class=&quot;string&quot;&gt;&quot;2.2.0&quot;&lt;/span&gt;]]
  &lt;span class=&quot;constant&quot;&gt;:profiles&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:dev&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:dependencies&lt;/span&gt; [[ring/ring-jetty-adapter &lt;span class=&quot;string&quot;&gt;&quot;1.0.2&quot;&lt;/span&gt;]
                                  [ring/ring-devel &lt;span class=&quot;string&quot;&gt;&quot;1.0.2&quot;&lt;/span&gt;]]}
             &lt;span class=&quot;constant&quot;&gt;:1.2&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:dependencies&lt;/span&gt; [[org.clojure/clojure &lt;span class=&quot;string&quot;&gt;&quot;1.2.1&quot;&lt;/span&gt;]]}
             &lt;span class=&quot;constant&quot;&gt;:1.4&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:dependencies&lt;/span&gt; [[org.clojure/clojure &lt;span class=&quot;string&quot;&gt;&quot;1.4.0-beta1&quot;&lt;/span&gt;]]}}
  &lt;span class=&quot;constant&quot;&gt;:aliases&lt;/span&gt; {&lt;span class=&quot;string&quot;&gt;&quot;all&quot;&lt;/span&gt; [&lt;span class=&quot;string&quot;&gt;&quot;with-profile&quot;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;dev,1.2:dev:dev,1.4&quot;&lt;/span&gt;]}&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;Here you can see the &lt;tt&gt;:dev&lt;/tt&gt; profile with some handy
  dependencies used for testing, but there are also profiles
  for &lt;tt&gt;:1.2&lt;/tt&gt; and &lt;tt&gt;:1.4&lt;/tt&gt; that can be used like lein
  multi. The &lt;tt&gt;with-profile&lt;/tt&gt; task is used to apply alternate
  profiles to a given task run:&lt;/p&gt;

&lt;pre&gt;$ lein with-profile dev,1.2 test
Performing task 'test' with profile(s): 'dev,1.2'

Testing clj-http.test.client

Testing clj-http.test.cookies

Testing clj-http.test.core

Ran 47 tests containing 175 assertions.
0 failures, 0 errors.&lt;/pre&gt;

&lt;p&gt;You can see that commas allow multiple profiles to be specified
  at once. You can also use colons to chain together profile sets
  sequentially:&lt;/p&gt;

&lt;pre&gt;$ lein with-profile dev:dev,1.2:dev,1.4 test
Performing task 'test' with profile(s): 'dev'
[...]
Performing task 'test' with profile(s): 'dev,1.2'
[...]
Performing task 'test' with profile(s): 'dev,1.4'
[...]&lt;/pre&gt;

&lt;p&gt;Of course, since &lt;tt&gt;with-profile&lt;/tt&gt; is a higher-order task, it
  can accept any other task as an argument, not just &lt;tt&gt;test&lt;/tt&gt;.
  So you could use it for deploying to different environments or
  any place where you'd want an alternate set of project
  configuration values.&lt;/p&gt;

&lt;h4&gt;Aliases&lt;/h4&gt;

&lt;p&gt;You'll also notice that the &lt;tt&gt;:aliases&lt;/tt&gt; entry in the
  &lt;tt&gt;defproject&lt;/tt&gt; above maps a string to a vector. This is a new
  feature about which I am unreasonably pleased. You've always been
  able to add aliases for Leiningen tasks; this is how &lt;kbd&gt;lein
  halp&lt;/kbd&gt; has worked. But now you can actually alias a string to
  a partial application of a task:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;constant&quot;&gt;:aliases&lt;/span&gt; {&lt;span class=&quot;string&quot;&gt;&quot;all&quot;&lt;/span&gt; [&lt;span class=&quot;string&quot;&gt;&quot;with-profile&quot;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;dev,1.2:dev:dev,1.4&quot;&lt;/span&gt;]}&lt;/pre&gt;

&lt;p&gt;This means that &lt;kbd&gt;all test&lt;/kbd&gt; translates into
  calling &lt;kbd&gt;with-profile dev,1.2:dev:dev,1.4 test&lt;/kbd&gt;. But
  partially-applied aliases have other uses as well:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;constant&quot;&gt;:aliases&lt;/span&gt; {&lt;span class=&quot;string&quot;&gt;&quot;reflect&quot;&lt;/span&gt; [&lt;span class=&quot;string&quot;&gt;&quot;assoc&quot;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;:warn-on-reflection&quot;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;compile&quot;&lt;/span&gt;]}&lt;/pre&gt;

&lt;p&gt;This allows you to invoke &lt;kbd&gt;lein reflect&lt;/kbd&gt; to get a list
  of all your reflection warnings. Note that in this
  case &lt;kbd&gt;assoc&lt;/kbd&gt; refers to the task that comes from
  the &lt;a href=&quot;https://github.com/technomancy/lein-assoc&quot;&gt;lein-assoc&lt;/a&gt;
  plugin, not the &lt;kbd&gt;clojure.core/assoc&lt;/kbd&gt; function. Each
  string in the alias vector is interpreted the same way as if it
  were a direct command-line argument to the &lt;kbd&gt;lein&lt;/kbd&gt; script,
  which is why strings must be used. In this case reading it into
  keywords and booleans happens inside the &lt;kbd&gt;assoc&lt;/kbd&gt;
  task.&lt;/p&gt;

&lt;p&gt;And that's one of the neat things about having tasks as functions.&lt;/p&gt;

&lt;h4&gt;Repl&lt;/h4&gt;

&lt;p&gt;The &lt;kbd&gt;repl&lt;/kbd&gt; task has been rewritten from the ground-up by
  Colin Jones. The new version supports a JVM-native readline
  implementation as well as thorough completion and nREPL support.&lt;/p&gt;

&lt;h4&gt;Preview&lt;/h4&gt;

&lt;p&gt;By this point I'm sure you're thinking to yourself, &quot;Gosh, that
  sounds super; I wish I could use it now!&quot; In fact, Leiningen 2 is
  already pretty usable and stable if you don't mind running from
  git. You need a copy of Leiningen 1.x around to bootstrap it, but
  running &lt;kbd&gt;lein install&lt;/kbd&gt; inside the &lt;tt&gt;leiningen-core&lt;/tt&gt;
  directory should get you to the point where you can
  symlink &lt;tt&gt;bin/lein&lt;/tt&gt; to somewhere on your path and have it
  work. I recommend linking it as &lt;tt&gt;lein2&lt;/tt&gt; for the time being
  since you'll probably still need an installation of 1.x easily
  accessible.&lt;/p&gt;

&lt;p&gt;Of course, being a major version increment it's got some
  backwards-incompatibilities. Fresh from witnessing the very bumpy
  transition to Clojure 1.3, I'd rather avoid that for Leiningen 2,
  so taking a cue from golang's &lt;tt&gt;gofix&lt;/tt&gt; tool, I've
  released the &lt;tt&gt;lein-precate&lt;/tt&gt; plugin.&lt;/p&gt;

&lt;p&gt;Precate, obviously, is the opposite of deprecate. The idea is
  that you could run it on your project and have it spit out a new
  &lt;tt&gt;project.clj&lt;/tt&gt; which would be compatible with Leiningen 2.
  It's not perfect, but it should provide you with a starting point
  for your transition:&lt;/p&gt;

&lt;pre&gt;$ lein plugin install lein-precate 0.3.0
$ cat project.clj # the original 1.x-compatible version:
(defproject clojure-http-client &quot;1.1.1-SNAPSHOT&quot;
  :description &quot;An HTTP client for Clojure.&quot;
  :source-path &quot;src/clj&quot;
  :extra-classpath-dirs [&quot;dumb-stuff&quot;]
  :dev-dependencies [[swank-clojure &quot;1.3.4&quot;]]
  :dependencies [[org.clojure/clojure &quot;1.2.1&quot;]
                 [org.clojure/clojure-contrib &quot;1.2.0&quot;]])

$ lein precate # let's see how that would look for Leiningen 2
(defproject clojure-http-client &quot;1.1.1-SNAPSHOT&quot;
  :description &quot;An HTTP client for Clojure.&quot;
  :source-paths [&quot;src/clj&quot;]
  :dependencies {org.clojure/clojure &quot;1.2.1&quot;, 
                 org.clojure/clojure-contrib &quot;1.2.0&quot;}
  :profiles {:dev
              {:resource-paths [&quot;dumb-stuff&quot;],
               :dependencies {swank-clojure &quot;1.3.4&quot;}}}
  :min-lein-version &quot;2.0.0&quot;)&lt;/pre&gt;

&lt;p&gt;Unfortunately that output had to be manually edited a bit for cosmetic
  reasons; Clojure's pretty-printer doesn't really know what to do
  with &lt;code&gt;defproject&lt;/code&gt; forms. But it should cover most of
  the changes necessary to take your project into the exciting new
  world of Leiningen 2. The biggest changes will come from the move
  from &lt;tt&gt;:dev-dependencies&lt;/tt&gt; to &lt;tt&gt;:dependencies&lt;/tt&gt; in
  the &lt;tt&gt;:dev&lt;/tt&gt; profile. But this is not a foolproof translation
  since in Leiningen 2 &lt;tt&gt;:dependencies&lt;/tt&gt; only run in the
  context of the project itself, while in Leiningen
  1 &lt;tt&gt;:dev-dependencies&lt;/tt&gt; ran in both Leiningen and the
  project. In retrospect this was a design mistake, but there are a
  number of plugins out there that take advantage of this fact and
  will need to be split into separate artifacts for the parts that
  run in Leiningen vs the parts that run in the project. I've taken
  some time to adapt some of the most commonly-used plugins for this
  change, but I'm sure I missed some of the more obscure ones.&lt;/p&gt;

&lt;p&gt;The plan from here is to polish off a few more features and cut a
  preview release. The preview will still be missing a handful of
  the more obscure features from Leiningen 1 like shell wrappers and
  selective transitive class file cleaning, but for the vast
  majority of projects it should be usable for everyday work. See
  the &quot;post-preview&quot; section
  of &lt;a href=&quot;https://github.com/technomancy/leiningen/blob/master/todo.org&quot;&gt;the
  todo file&lt;/a&gt; for details on what's remaining. The hope is to have
  it ready at latest by
  the &lt;a href=&quot;http://clojurewest.org/&quot;&gt;Clojure/West
  conference&lt;/a&gt;. Enjoy!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: Leiningen
    2.0.0 &lt;a href=&quot;https://github.com/technomancy/leiningen/blob/2.0.0/NEWS.md&quot;&gt;has
    been released&lt;/a&gt;! See
    the &lt;a href=&quot;https://github.com/technomancy/leiningen/wiki/Upgrading&quot;&gt;upgrade
    guide&lt;/a&gt;.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20an%20interview%20is%20posted</id>
    <published>2012-01-27T03:58:25Z</published>
    <updated>2012-01-27T03:58:25Z</updated>

    <link href="http://technomancy.us/157" rel="alternate" type="text/html"/>
    <title>in which an interview is posted</title>
    <content type="html">
      &lt;p&gt;The folks over at The Setup just posted
  &lt;a href=&quot;http://phil.hagelberg.usesthis.com/&quot;&gt;an interview&lt;/a&gt;
  with me wherein I rant about hardware, interactivity, and
  Emacs.&lt;/p&gt;

&lt;p&gt;Note: this was written up over a month ago; if I were interviewed
  today I couldn't help but mention
  the &lt;a href=&quot;http://nixos.org/nix&quot;&gt;Nix&lt;/a&gt; package manager. I use
  it on my Debian Squeeze system to complement &lt;tt&gt;apt-get&lt;/tt&gt;; I
  get all the system-level stuff that has to be stable from Debian
  and anything that needs to be fresh from Nix.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20local%20hacking%20locations%20are%20surveyed</id>
    <published>2011-12-31T02:25:58Z</published>
    <updated>2011-12-31T02:25:58Z</updated>

    <link href="http://technomancy.us/156" rel="alternate" type="text/html"/>
    <title>in which local hacking locations are surveyed</title>
    <content type="html">
      &lt;p&gt;I'm lucky to have the chance to do my job remotely from wherever
  I like. Half the time I work out
  of &lt;a href=&quot;http://www.flickr.com/photos/technomancy/tags/laboratory&quot;&gt;my
  code lab&lt;/a&gt;, which is a converted shed in my back yard. But I
  venture out fairly frequently to local coffee shops. Here are a
  few of my favourites. I should mention that I'm a big fan of
  single-origin pour-over brews, so the list is biased in favour of
  places which serve that well rather than the more traditional
  espresso drinks. All these have at least one location in North
  Seattle.&lt;/p&gt;

&lt;h4&gt;&lt;a href=&quot;http://neptunecoffee.com&quot;&gt;Neptune&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;I'm here a lot because it's the only one on this list I don't
  need to get on the freeway for. The decor is a bit sparse, but
  they're quite friendly here and serve a mean pour-over. They've
  been known to even
  serve &lt;a href=&quot;http://www.youtube.com/watch?v=VbqJKDZNyic&quot;&gt;syphon-brewed
  coffee&lt;/a&gt;, which is always a treat to watch when they fire up the
  bunsen burner. The proprietor also
  runs &lt;a href=&quot;http://tehcoffee.com/&quot;&gt;tehcoffee.com&lt;/a&gt;, a
  (Heroku-hosted) site for competition-level baristas can rank and
  review others. The Greenwood neighborhood is great too; lots of
  lunch options and bookstores to browse. Bonus fact: Allie, the
  inventor
  of &lt;a href=&quot;http://hyperboleandahalf.blogspot.com/2010/04/alot-is-better-than-you-at-everything.html&quot;&gt;the
  mythical Alot beast&lt;/a&gt; has a blend here named after her.&lt;/p&gt;

&lt;img src=&quot;i/milstead.jpg&quot; alt=&quot;milstead views&quot; align=&quot;right&quot; /&gt;

&lt;h4&gt;&lt;a href=&quot;http://www.milsteadandco.com/&quot;&gt;Milstead &amp; Co.&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;This one has only opened relatively recently, but it's made a
  splash for a number of things, not the least of which is the
  spectacular waterfront view from under the Aurora bridge. The
  selection here is excellent; this is the only place on this list
  that serves beans from multiple roasters, and they offer plenty of
  variety in brewing methods. The interior is spacious with high
  ceilings; it's very inviting. Mr. Milstead himself is usually
  around, and his excitement about his craft is infectious and
  obvious once you get him talking. I'd be here all the time if it
  weren't the furthest (south) from where I live.&lt;/p&gt;

&lt;h4&gt;&lt;a href=&quot;http://zokacoffee.com&quot;&gt;Zoka&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;This one is my go-to place. I often run into people I know here,
  (and sometimes &lt;a href=&quot;http://theoatmeal.com/&quot;&gt;people I
  don't&lt;/a&gt;), and we hold
  the &lt;a href=&quot;http://seajure.github.com&quot;&gt;Seattle Clojure group&lt;/a&gt;
  meetings here as well. The University District location in
  particular works well for meetings just because it's so spacious;
  I don't think I've ever seen a larger coffee shop. It's a popular
  place for students to study, so it's pretty quiet in the back. It
  can be crowded at times though, probably based on the school
  schedule. They've got a great selection sourced from all over the
  world that rotates every so often. It's always fun to
  read &lt;a href=&quot;http://www.zokacoffee.com/blog/2011/03/jeffs-trip-to-nicaragua.html&quot;&gt;their
  blog posts about traveling to visit the growers&lt;/a&gt;. The &quot;Zoka
  bars&quot; they serve here are a ridiculously rich treat. The decor
  here is very warm and comfortable with wood paneling
  everywhere.&lt;/p&gt;

&lt;img src=&quot;i/trabant.jpg&quot; alt=&quot;trabant&quot; align=&quot;left&quot; /&gt;

&lt;h4&gt;&lt;a href=&quot;http://trabantcoffee.com&quot;&gt;Trabant&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;I don't come here often because the parking situation is awful,
  but if you are in the area (or taking the bus), definitely check
  out Trabant. They've got good chai, but the thing that keeps
  bringing me back here is
  the &lt;a href=&quot;http://www.slate.com/articles/life/drink/2008/03/could_a_coffee_maker_be_worth_11000.html&quot;&gt;Clover
  machine&lt;/a&gt;. This contraption is capable of extracting rich
  flavours out of a bean like nothing else. Unfortunately Starbucks
  purchased the company that sold them, so there aren't very many
  places with Clovers that offer good beans. (As the owner of
  Trabant said about the acquisition, &quot;wearing Air Jordans doesn't
  make you play like Michael Jordan.&quot;) Anyway, the downtown Trabant
  is very spare, minimalist, and quiet, while the one in the
  University district (pictured) is bustling and quirky.&lt;/p&gt;

&lt;h4&gt;Honorable Mentions&lt;/h4&gt;

&lt;dl&gt;
  &lt;dt&gt;&lt;a href=&quot;http://www.espressovivace.com/&quot;&gt;Vivace&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;I'm of the opinion that Vivace has the best espresso in
    Seattle. It's too far for me to visit regularly, but members of
    the famed
    &lt;a href=&quot;http://seattlerb.org&quot;&gt;Seattle Ruby Brigade&lt;/a&gt; can
    often be found working here in the afternoon.&lt;/dd&gt;

  &lt;dt&gt;&lt;a href=&quot;http://www.seattlecoffeeworks.com/&quot;&gt;Seattle Coffee Works&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;I've only been here a couple times, but I was floored by
    their Yirgacheffe both times. They serve their pour-overs at a
    separate &quot;slow bar&quot; where the baristas are happy to geek out
    over their creations if you show interest. Again, this one's
    too far away (Pike St.) to make me a regular.&lt;/dd&gt;

  &lt;dt&gt;&lt;a href=&quot;http://www.greenbeancoffee.org/&quot;&gt;Green Bean&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;This one is actually a non-profit which supports a number of
    other non-profits on a rotating basis. The staff here are really
    friendly. I like to come here early and get breakfast with my
    family since they've got a kids' corner.&lt;/dd&gt;

  &lt;dt&gt;&lt;a href=&quot;http://www.chocolati.com/&quot;&gt;Chocolati&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;I don't even come here for their coffee, but their hot
    chocolate is superb. They've also got great truffles. The
    Greenwood location is a converted house that offers plenty of
    space to work, while the Greenlake location is a lot smaller but
    has nice views of the water.&lt;/dd&gt;
&lt;/Dal&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20version%20two%20seems%20a%20treacherous%20stage</id>
    <published>2011-12-01T07:15:11Z</published>
    <updated>2011-12-01T07:15:11Z</updated>

    <link href="http://technomancy.us/155" rel="alternate" type="text/html"/>
    <title>in which version two seems a treacherous stage</title>
    <content type="html">
      &lt;p&gt;I recently got back from
  the &lt;a href=&quot;http://clojure-conj.org/&quot;&gt;Clojure Conj&lt;/a&gt; conference
  in Raleigh. The sessions were great, but the main highlight for me
  was meeting with &lt;a href=&quot;http://flatland.org/&quot;&gt;the developers of
  Cake&lt;/a&gt; (an alternate Clojure build tool) and discussing how we
  could collaborate on the future of build tools in Clojure. As
  was &lt;a href=&quot;http://groups.google.com/group/leiningen/browse_thread/thread/5a79d02198a91b91&quot;&gt;announced&lt;/a&gt;
  &lt;a href=&quot;https://groups.google.com/group/clojure-cake/browse_thread/thread/186ec36c2426996e&quot;&gt;elsewhere&lt;/a&gt;,
  we will be taking some features from Cake and merging them into
  Leiningen 2.0 as well as just having more hackers involved in
  development efforts.&lt;/p&gt;

&lt;p&gt;The development of Leiningen 1.x pretty much just fell out of the
  usage patterns we saw during my time at Sonian as an early adopter
  of Clojure. We used Maven for eight months, tried to make it work,
  and then took our experience from the pain we saw there to
  Leiningen. Some features (especially checkout dependencies) arose
  directly from finding certain operations with multi-module Maven
  builds extremely cumbersome. But in general nearly everything
  about Leiningen so far has been obvious. I wouldn't say it
  practically wrote itself, but once the central model of a project
  map and resolving/applying tasks from defns was established, the
  only really tricky thing was being strict about establishing a
  narrow scope and knowing when to avoid adding features.&lt;/p&gt;

&lt;blockquote cite=&quot;http://twitter.com/#!/cemerick/status/131414628474437633&quot;&gt;
  &lt;p&gt;&quot;Version 2&quot; is the most dangerous phase in a software
    project's life.&lt;/p&gt;
  &lt;footer&gt;— &lt;a href=&quot;http://twitter.com/#!/cemerick/status/131414628474437633&quot;&gt;Chas
  Emerick&lt;/a&gt;&lt;/footer&gt;
&lt;/blockquote&gt;

&lt;p&gt;So now
  we're &lt;a href=&quot;https://github.com/technomancy/leiningen/wiki/VersionTwo&quot;&gt;digging
  into the question of what bigger-picture changes could be made&lt;/a&gt;
  to improve Leiningen if we leave backwards-compatibility out of
  it. The biggest improvement we've seen implemented so far is
  switching over dependency resolution to the Aether library
  via &lt;a href=&quot;https://github.com/cemerick/pomegranate&quot;&gt;Chas
  Emerick's Pomegranate library&lt;/a&gt;. Aether is a library that
  contains all the dependency management features from Maven
  extracted into an independent module&amp;mdash;basically designed with
  exactly Leiningen's use case in mind.&lt;/p&gt;

&lt;p&gt;But there's more coming. Cake has had the ability to run project
  code &lt;a href=&quot;https://github.com/flatland/classlojure&quot;&gt;in the same
  process as the build tool&lt;/a&gt;, significantly speeding up many
  operations, which is in the process of being ported over to
  Leiningen. I've been working on explicitly delimiting the parts of
  Leiningen that comprise its public API as part
  of &lt;a href=&quot;https://github.com/technomancy/leiningen/tree/master/leiningen-core&quot;&gt;a
  separate &quot;leiningen-core&quot; library&lt;/a&gt; which other tools can depend
  upon. We're looking at adding something like &quot;profiles&quot; do bundle
  up configuration sets which can be activated in given
  contexts.&lt;/p&gt;

&lt;p&gt;With all these ideas flying around I hope we can buckle down and
  get some solid design discussions resulting in well-factored
  features. If you've been looking to contribute, now is a great
  time. Join the &lt;tt&gt;#leiningen&lt;/tt&gt; channel on Freenode and hop on
  the &lt;a href=&quot;http://groups.google.com/group/leiningen/&quot;&gt;mailing
  list&lt;/a&gt;.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20the%20lesser-known%20are%20brought%20to%20the%20forefront</id>
    <published>2011-10-28T17:21:54Z</published>
    <updated>2011-10-28T17:21:54Z</updated>

    <link href="http://technomancy.us/154" rel="alternate" type="text/html"/>
    <title>in which the lesser-known are brought to the forefront</title>
    <content type="html">
      &lt;p&gt;In my time hacking in Clojure I've found a bunch of
  under-appreciated Clojure libraries of which I'm rather fond. I
  thought it'd be helpful to share, so here they are:&lt;/p&gt;

&lt;h4&gt;&lt;a href=&quot;https://github.com/maravillas/lein-multi&quot;&gt;lein-multi&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;As support for Clojure 1.3 becomes widespread, it becomes more
  important for projects to think about backwards compatibility. You
  can specify a range of versions for Clojure in &lt;tt&gt;project.clj&lt;/tt&gt;
  with &lt;code&gt;[org.clojure/clojure &quot;[1.2.0,1.3.0]&quot;]&lt;/code&gt;, but when
  you're developing, it will just find the latest and pull that in,
  so it's easy for dependence on 1.3-specific features to sneak
  in.&lt;/p&gt;

&lt;p&gt;Enter lein-multi, a plugin for running arbitrary tasks against
  multiple dependency sets. It's most commonly used with
  the &lt;kbd&gt;test&lt;/kbd&gt; task, but it's a general higher-order task
  that can be applied to any other just as well.
  Seeing &lt;tt&gt;lein-multi&lt;/tt&gt; in someone's project.clj is a good
  indicator that they take backwards-compatibility seriously.
&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: Leiningen 2 supports this out of the box using
  profiles, so the &lt;tt&gt;lein-multi&lt;/tt&gt; plugin is deprecated.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[lein-multi &quot;1.0.0&quot;]&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;&lt;a href=&quot;https://github.com/mmcgrana/clj-stacktrace&quot;&gt;clj-stacktrace&lt;/a&gt;&lt;/h4&gt;

&lt;img src=&quot;i/clj-stacktrace.png&quot; alt=&quot;stack trace&quot; align=&quot;right&quot; /&gt;

&lt;p&gt;The most common complaint I hear about developing in Clojure is
  the fact that its error messages often obscure the true cause of
  the problem. While practice helps here, it's still true that
  there's a lot of irrelevant detail here that can overwhelm the
  trained eye. Mark McGranaghan's clj-stacktrace library does a
  great job at summarizing stack traces by aligning each frame,
  making the namespace distinct from the function name, and even
  coloring frames differently based on whether they come from
  Clojure, Java, or user code. The next version should let you
  filter out frames that are deemed irrelevant on a per-project
  basis.&lt;/p&gt;

&lt;p&gt;To use it in your project's repl as well as
  in &lt;code&gt;clojure.test&lt;/code&gt; error reporting, place this code
  in &lt;tt&gt;~/.lein/init.clj&lt;/tt&gt; after running &lt;kbd&gt;$ lein plugin
  install clj-stacktrace 0.2.4&lt;/kbd&gt;:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt;require&lt;/span&gt; 'leiningen.hooks.clj-stacktrace-test&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;settings&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:repl-options&lt;/span&gt; [&lt;span class=&quot;constant&quot;&gt;:init&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt;require&lt;/span&gt; 'clj-stacktrace.repl&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;
                              &lt;span class=&quot;constant&quot;&gt;:caught&lt;/span&gt; 'clj-stacktrace.repl/pst+]}&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;&lt;/pre&gt;

&lt;h4&gt;&lt;a href=&quot;https://github.com/scgilardi/slingshot&quot;&gt;slingshot&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;Another common question you hear is &quot;How do I generate custom
  exception classes?&quot;. It's awkward and somewhat un-idiomatic to do
  this, so people generally try to get by with the Exception
  classes that ship with the JDK. But why shouldn't exceptions get
  the same level of dynamicity and flexibility that Clojure affords
  other data types?&lt;/p&gt;

&lt;p&gt;This is basically the question Slingshot addresses. It provides
  enhanced &lt;code&gt;try+&lt;/code&gt; and &lt;code&gt;throw+&lt;/code&gt; counterparts to
  the built-in error mechanisms that let you throw arbitrary data
  types like maps. Then rather than dispatching in your catch blocks
  based on class, you can use arbitrary predicates. You can even
  perform destructuring on maps that are thrown. It's a big step up
  in expressiveness:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;defn&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;asplode!&lt;/span&gt; []
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;throw+ {&lt;span class=&quot;constant&quot;&gt;:bad?&lt;/span&gt; true &lt;span class=&quot;constant&quot;&gt;:tachyon-level&lt;/span&gt; 21}&lt;span class=&quot;esk-paren&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;defn&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;ignorable?&lt;/span&gt; [e]
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;builtin&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;:silent&lt;/span&gt; e&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;constant&quot;&gt;:fatal?&lt;/span&gt; e&lt;span class=&quot;esk-paren&quot;&gt;))))&lt;/span&gt;

&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;try+ &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;asplode!&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;builtin&quot;&gt;catch&lt;/span&gt; ignorable? _&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;builtin&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;:bad?&lt;/span&gt; e
    &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;log/warn &lt;span class=&quot;string&quot;&gt;&quot;Bummer dude!&quot;&lt;/span&gt; e&lt;span class=&quot;esk-paren&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;builtin&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;constant&quot;&gt;:fatal?&lt;/span&gt; {&lt;span class=&quot;constant&quot;&gt;:keys&lt;/span&gt; [exit-code]}
    &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;preprocessor&quot;&gt;System/exit&lt;/span&gt; exit-code&lt;span class=&quot;esk-paren&quot;&gt;)))&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: The equivalent of the exception-throwing side of
  Slingshot has been included in Clojure 1.4 using
  the &lt;code&gt;ex-info&lt;/code&gt; and &lt;code&gt;ex-data&lt;/code&gt; functions, so
  unless you are targeting old versions of Clojure you should use
  those instead. Nothing has been added on the exception handling
  side though, so Slingshot's &lt;code&gt;try+&lt;/code&gt; macro is still very
  useful.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[slingshot &quot;0.8.0&quot;]&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;&lt;a href=&quot;https://github.com/brentonashworth/lein-difftest&quot;&gt;lein-difftest&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;If you've ever written a test where you expect two lengthy data
  structures to be equal, you'll remember how annoying it is to try
  to compare the failure message where &quot;expected&quot; and &quot;actual&quot; are
  each spat out on a single line and you're supposed to try to hunt
  down the difference. Using &lt;kbd&gt;lein difftest&lt;/kbd&gt; makes it easy:&lt;/p&gt;

&lt;a href=&quot;https://github.com/brentonashworth/lein-difftest&quot;&gt;
  &lt;img src=&quot;i/lein-difftest.png&quot; alt=&quot;difftest example&quot; /&gt;&lt;/a&gt;

&lt;p&gt;It also uses &lt;tt&gt;clj-stacktrace&lt;/tt&gt; to report errors. This one
  is better off installed as a user-level plugin since you're
  likely to want to be able to use the &lt;kbd&gt;difftest&lt;/kbd&gt; task
  across all your projects:&lt;/p&gt;

&lt;p&gt;&lt;kbd&gt;$ lein plugin install lein-difftest 1.3.7&lt;/kbd&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: Use version 2.0.0 of &lt;tt&gt;lein-difftest&lt;/tt&gt; for
  compatibility with Leiningen 2.x.&lt;/p&gt;

&lt;h4&gt;&lt;a href=&quot;https://github.com/joegallo/robert-bruce&quot;&gt;robert-bruce&lt;/a&gt;&lt;/h4&gt;

&lt;p&gt;From the do-one-thing-and-do-it-well department, we have Robert
  Bruce, which concerns itself only with determinedly retrying a
  given function. It provides all the options you could imagine for
  how to perform the retries, including pausing between them (with
  exponential decay on backoffs), retry limits, callbacks on
  failure, and so on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;[robert/bruce &quot;0.7.1&quot;]&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;Happy hacking!&lt;/h4&gt;

&lt;p&gt;Do you have a favourite that I've missed here? Leave a comment
  about it. Remember that the version numbers provided here are
  current as of the time of this writing but may be outdated when
  you read this.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20version%20two%20turns%20the%20inside%20out</id>
    <published>2011-09-28T04:12:54Z</published>
    <updated>2011-09-28T04:12:54Z</updated>

    <link href="http://technomancy.us/153" rel="alternate" type="text/html"/>
    <title>in which version two turns the inside out</title>
    <content type="html">
      &lt;p&gt;I just got back last week from
  the &lt;a href=&quot;https://thestrangeloop.com/&quot;&gt;Strange Loop
  conference&lt;/a&gt; in St. Louis. The conference showcased some really
  quality keynotes and session talks, but I had been invited to give
  a workshop teaching Emacs, which was a blast. The workshop was
  three hours long, but I still felt like I was zooming
  through. &lt;a href=&quot;http://p.hagelb.org/getting-cozy-with-emacs.org.html&quot;&gt;My
  outline&lt;/a&gt; is available, though it may not be all that useful
  unless you were there.&lt;/p&gt;

&lt;img src=&quot;i/cozy-with-emacs.jpg&quot; alt=&quot;getting cozy&quot; align=&quot;right&quot; /&gt;

&lt;p&gt;The talk focused on the task of crafting your own set of
  dotfiles. Naturally I did talk some about
  the &lt;a href=&quot;https://github.com/technomancy/emacs-starter-kit&quot;&gt;Emacs
  Starter Kit&lt;/a&gt;, though my approach there has changed fairly
  radically. I've been developing version 2 of the Starter Kit
  since the beginning of the summer, but now I think it's time to
  make the release official and give it wider exposure.&lt;/p&gt;

&lt;h4&gt;The Emacs Starter Kit, version 2&lt;/h4&gt;

&lt;p&gt;Version 2 of the Starter Kit turns things inside out. The
  original version was very prescriptive; you used it by cloning a
  git repo to use as your entire &lt;tt&gt;~/.emacs.d&lt;/tt&gt; directory, and
  you fit your own customizations inside the structure it
  provided. This was mostly due to the fact that when it was
  released the packaging story for elisp was very immature. Use of
  package.el was not very widespread, so the Starter Kit had to
  include a copy as well as bundle a bunch of libraries that had not
  been packaged yet. As it turned out, I think the Starter Kit had a
  role in helping popularize package.el.&lt;/p&gt;

&lt;p&gt;A lot has changed since 2008&amp;mdash;primarily
  that &lt;a href=&quot;/133&quot;&gt;package.el has been included in Emacs&lt;/a&gt; and
  &lt;a href=&quot;http://marmalade-repo.org&quot;&gt;Marmalade&lt;/a&gt;
  has &lt;a href=&quot;/144&quot;&gt;picked up lots of steam&lt;/a&gt;, becoming the
  de-facto community package source with over 250 packages. It's now
  feasible for the Starter Kit to be distributed as just another
  package, leaving you free to structure your dotfiles however you
  like. All it takes is setting yourself up to get packages from
  Marmalade:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;require&lt;/span&gt; '&lt;span class=&quot;constant&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;add-to-list 'package-archives
             '&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;string&quot;&gt;&quot;marmalade&quot;&lt;/span&gt; . &lt;span class=&quot;string&quot;&gt;&quot;http://marmalade-repo.org/packages/&quot;&lt;/span&gt;&lt;span class=&quot;esk-paren&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;package-initialize&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;&lt;/pre&gt;

&lt;img src=&quot;/i/langley-rocket.jpg&quot; alt=&quot;rocket, man&quot; align=&quot;left&quot; /&gt;

&lt;p&gt;Place that in &lt;tt&gt;~/.emacs.d/init.el&lt;/tt&gt; and evaluate it with
  &lt;kbd&gt;M-x eval-buffer&lt;/kbd&gt;. From there you can run &lt;kbd&gt;M-x
  package-refresh-contents&lt;/kbd&gt; to pull in the latest package lists
  (analogous to &lt;kbd&gt;apt-get update&lt;/kbd&gt;) followed by &lt;kbd&gt;M-x
  package-install starter-kit&lt;/kbd&gt;. And then you're set.&lt;/p&gt;

&lt;p&gt;However, it's unlikely that the starter-kit is the only package
  you're interested in. Rather than picking packages by hand, I'm a
  fan of keeping a list of all the packages you use in your
  dotfiles. That way when you move to a new machine, you won't have
  to remember what packages you've come to depend upon; checking out
  your dotfiles on a new machine will pull in everything you've
  specified. Here's my list along with some code to handle it:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;not package-archive-contents&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;package-refresh-contents&lt;span class=&quot;esk-paren&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;defvar&lt;/span&gt; &lt;span class=&quot;variable-name&quot;&gt;my-packages&lt;/span&gt; '&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;starter-kit starter-kit-lisp starter-kit-eshell
                                  starter-kit-bindings scpaste
                                  clojure-mode clojure-test-mode
                                  markdown-mode yaml-mode tuareg
                                  marmalade oddmuse scpaste&lt;span class=&quot;esk-paren&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;dolist&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;p my-packages&lt;span class=&quot;esk-paren&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;not &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;package-installed-p p&lt;span class=&quot;esk-paren&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;esk-paren&quot;&gt;(&lt;/span&gt;package-install p&lt;span class=&quot;esk-paren&quot;&gt;)))&lt;/span&gt;&lt;/pre&gt;

&lt;h4&gt;What's Changed&lt;/h4&gt;

&lt;p&gt;Being a 2.0 release, you can't expect 100% backwards compatibility
  with the old Starter Kit. In particular, it is much more
  modular. The base of the Starter Kit simply gives you better
  default settings and a few new commands. There are separate
  modules supporting various languages like Ruby, Lisp (Clojure,
  Emacs Lisp, Scheme, and Common Lisp), and Javascript, though right
  now the Lisp one is the most polished. If anyone is interested in
  helping maintain alternate language modules, please contact
  me &lt;a href=&quot;https://github.com/technomancy&quot;&gt;on
  Github&lt;/a&gt;. Key bindings have also been spun out into their own module
  since they don't follow Emacs binding conventions strictly.&lt;/p&gt;

&lt;p&gt;There is also no more &lt;tt&gt;elpa-to-submit&lt;/tt&gt; directory; if you
  rely on packages that aren't on Marmalade yet, it's up to you to
  either check them into your own dotfiles or better yet upload them
  yourself. There's also no more explicit &lt;tt&gt;recentf&lt;/tt&gt; support
  since a new feature called &lt;tt&gt;ido-use-virtual-buffers&lt;/tt&gt; allows
  recent files to show up in the regular &lt;kbd&gt;C-x b&lt;/kbd&gt; buffer
  switcher, simplifying things. The addition of
  the &lt;a href=&quot;https://github.com/technomancy/ido-ubiquitous&quot;&gt;ido-ubiquitous&lt;/a&gt;
  package makes it so regular completion gets enhanced with ido
  magic, so functionality like &lt;tt&gt;ido-imenu&lt;/tt&gt; is no longer
  needed.&lt;/p&gt;

&lt;p&gt;The other breakage is that the Starter Kit is now intended only
  for use with Emacs 24 onward. Yes, technically Emacs 24 has not
  yet been released, but it's in active pretest and is readily
  available for most platforms.[&lt;a href=&quot;#fn1&quot;&gt;1&lt;/a&gt;]. I've been
  using it since 23 was released and have had no trouble with it.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;[&lt;a name=&quot;fn1&quot;&gt;1&lt;/a&gt;] It's not hard to
  compile &lt;a href=&quot;http://github.com/emacsmirror/emacs&quot;&gt;from
  source&lt;/a&gt;, but precompiled versions are readily available for
  &lt;a href=&quot;http://emacs.naquadah.org/&quot;&gt;Debian-based
  systems&lt;/a&gt;, &lt;a href=&quot;http://emacsformacosx.com/builds&quot;&gt;Mac OS
  X&lt;/a&gt;, and
  &lt;a href=&quot;http://code.google.com/p/emacs-for-windows/updates/list&quot;&gt;Windows&lt;/a&gt;.&lt;/p&gt;
    </content>
  </entry>
  
  <entry xml:base="http://technomancy.us">
    <author><name>Phil Hagelberg</name></author>
    <id>tag:technomancy.us,2007:in%20which%20static%20types%20are%20friends,%20not%20foes</id>
    <published>2011-08-15T01:37:00Z</published>
    <updated>2011-08-15T01:37:00Z</updated>

    <link href="http://technomancy.us/152" rel="alternate" type="text/html"/>
    <title>in which static types are friends, not foes</title>
    <content type="html">
      &lt;p&gt;I'm a big fan of composability in user interfaces. Unix has
  traditionally been strong in this area with its culture of pipes
  and standard in/out, making it easy to chain together small tools
  with orthogonal purposes. Unfortunately this usually does not
  extend to GUI tools which often tend towards monolithic
  globs of functionality. There are a few exceptions, my favourites
  including &lt;a href=&quot;http://www.nongnu.org/xbindkeys/xbindkeys.html&quot;&gt;xbindkeys&lt;/a&gt;
  and &lt;a href=&quot;http://musicpd.org/&quot;&gt;mpd&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The most versatile of these I've found has
  been &lt;a href=&quot;http://tools.suckless.org/dmenu&quot;&gt;dmenu&lt;/a&gt;, a
  graphical option chooser in the style of Emacs' &lt;tt&gt;ido-mode&lt;/tt&gt;:
  it presents a list of options, and as you type it narrows to just
  the options which match the input that's been entered so far. I've
  built a number of tools on top of this including
  a &lt;a href=&quot;https://github.com/technomancy/dotfiles/blob/43e98156961a7592a1b809740d0af4f04d4835db/bin/music-choose&quot;&gt;music
  frontend to mpd&lt;/a&gt;
  and &lt;a href=&quot;https://github.com/technomancy/dotfiles/blob/master/bin/skyyy&quot;&gt;a
    script that allows me to perform a number of Skype actions from
    the keyboard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The problem is that dmenu is pretty minimalistic compared to
  ido. The main annoyance to me is that it doesn't support flex
  matching&amp;mdash;in other words, only exact matches are shown. In Emacs,
  &lt;tt&gt;ido&lt;/tt&gt; lets me match a few characters against the front of
  the string and a few against the tail. Any input is accepted as
  long as it uniquely identifies one of the choices. Since dmenu is
  written in C I didn't exactly relish the idea of picking up skills
  in that language to add this functionality, so I wrote a
  replacement in OCaml instead:&lt;/p&gt;

&lt;pre class=&quot;code&quot;&gt;&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;rec&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;read_lines&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; lines &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;try&lt;/span&gt; read_lines &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;read_line &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;::&lt;/span&gt; lines&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt; End_of_file &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; lines

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;variable-name&quot;&gt;lines &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt; read_lines &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[]&lt;/span&gt;

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;lines_matching&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; pattern matched line &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;variable-name&quot;&gt;_ &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Str&lt;/span&gt;.search_forward pattern line 0 &lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;in&lt;/span&gt; 
      line &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;::&lt;/span&gt; matched
    &lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt; Not_found &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; matched

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;escape&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;function&lt;/span&gt;
 &lt;span class=&quot;variable-name&quot;&gt; &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;' '&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;&quot;.*&quot;&lt;/span&gt;
  &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; c &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Char&lt;/span&gt;.escaped c

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; input &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;type&quot;&gt;Str&lt;/span&gt;.regexp &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;String&lt;/span&gt;.concat &lt;span class=&quot;string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;List&lt;/span&gt;.map escape input&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;matched&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; input lines &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;type&quot;&gt;List&lt;/span&gt;.fold_left &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;lines_matching &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;pattern input&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[]&lt;/span&gt; lines

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;rec&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;draw_matches&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; matches &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;type&quot;&gt;Graphics&lt;/span&gt;.open_graph &lt;span class=&quot;string&quot;&gt;&quot; 1440x15&quot;&lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;type&quot;&gt;Graphics&lt;/span&gt;.set_window_title &lt;span class=&quot;string&quot;&gt;&quot;erythrina&quot;&lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;type&quot;&gt;Graphics&lt;/span&gt;.draw_string &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;String&lt;/span&gt;.concat &lt;span class=&quot;string&quot;&gt;&quot; | &quot;&lt;/span&gt; matches&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;finish&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; input lines &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;type&quot;&gt;Graphics&lt;/span&gt;.close_graph &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;match&lt;/span&gt; matched input lines &lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt;
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; f &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;::&lt;/span&gt; _ &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; print_string f
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;butlast&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; input &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;List&lt;/span&gt;.rev input &lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt;
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; _ &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;::&lt;/span&gt; rest &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;List&lt;/span&gt;.rev rest

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;rec&lt;/span&gt; &lt;span class=&quot;function-name&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;variable-name&quot;&gt; input &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt;
  draw_matches &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;matched input lines&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Graphics&lt;/span&gt;.read_key &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;keyword&quot;&gt;with&lt;/span&gt;
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;comment-delimiter&quot;&gt;(* &lt;/span&gt;&lt;span class=&quot;comment&quot;&gt;enter &lt;/span&gt;&lt;span class=&quot;comment-delimiter&quot;&gt;*)&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;'\r'&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; finish input lines
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;comment-delimiter&quot;&gt;(* &lt;/span&gt;&lt;span class=&quot;comment&quot;&gt;escape &lt;/span&gt;&lt;span class=&quot;comment-delimiter&quot;&gt;*)&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;'\027'&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;type&quot;&gt;Graphics&lt;/span&gt;.close_graph &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;comment-delimiter&quot;&gt;(* &lt;/span&gt;&lt;span class=&quot;comment&quot;&gt;backspace &lt;/span&gt;&lt;span class=&quot;comment-delimiter&quot;&gt;*)&lt;/span&gt; &lt;span class=&quot;string&quot;&gt;'\b'&lt;/span&gt; &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; main &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;butlast input&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;comment-delimiter&quot;&gt;(* &lt;/span&gt;&lt;span class=&quot;comment&quot;&gt;any other &lt;/span&gt;&lt;span class=&quot;comment-delimiter&quot;&gt;*)&lt;/span&gt; c &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;-&amp;gt;&lt;/span&gt; main &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;type&quot;&gt;List&lt;/span&gt;.append input &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[&lt;/span&gt;c&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;tuareg-font-lock-governing&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;variable-name&quot;&gt;_ &lt;/span&gt;&lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;=&lt;/span&gt; main &lt;span class=&quot;tuareg-font-lock-operator&quot;&gt;[]&lt;/span&gt;&lt;/pre&gt;

&lt;p&gt;In 39 lines of OCaml I've achieved near feature-parity to the
  700 lines of C that make up dmenu. The only missing features are
  tab-completion and font/color customization, though I have added
  the flex-matching feature mentioned above that dmenu lacks, which
  makes tab completion less important.&lt;/p&gt;

&lt;p&gt;This is my second foray back into the land of static typing since
  university. (Supposedly I learned C++ in school, but I've long
  since mercifully forgotten it all.) I picked
  up &lt;a href=&quot;http://mirah.org&quot;&gt;Mirah&lt;/a&gt; last year
  and &lt;a href=&quot;/145&quot;&gt;spent a little more time hacking in it earlier
  this year for an Android app&lt;/a&gt;. While Mirah is a huge
  improvement over Java, its type inference unfortunately only
  extends to locals, (a common shortcoming of most JVM-hosted type
  systems from what I gather) meaning if you tend to write small
  methods you end up specifying all your types anyway. OCaml on the
  other hand infers all types
  using &lt;a href=&quot;http://en.wikipedia.org/wiki/Type_inference#Hindley.E2.80.93Milner_type_inference_algorithm&quot;&gt;Hindley
  Milner inference&lt;/a&gt;, allowing the types to be effectively
  invisible.&lt;/p&gt;

&lt;p&gt;I've only spent a few days writing OCaml, but I've got to say I'm
  impressed. The type system stayed out of the way, only making a
  fuss when I'd clearly made an error. Pattern matching is a dream:
  you'll notice that all the conditionals in the code above come
  from &lt;tt&gt;match&lt;/tt&gt; rather than &lt;tt&gt;if&lt;/tt&gt;. It's fast, it's
  pleasantly interactive, (especially
  via &lt;a href=&quot;http://marmalade-repo.org/packages/tuareg&quot;&gt;tuareg&lt;/a&gt;
  and Emacs) and the executables produced by the compiler are tiny
  and quick to start, which is valuable for anyone who spends a lot
  of time on the JVM and is looking for something to fill those
  pesky gaps for which the JVM is admittedly lousy.&lt;/p&gt;

&lt;p&gt;My only real complaints surround the fact that the standard
  library is slim and quirky. It throws exceptions in places you
  wouldn't expect, like hitting the end of the file while reading or
  searching for a regex when the text contains no match. The regex
  support is a bit odd, but that's not too surprising considering
  how old the language is.&lt;/p&gt;

&lt;p&gt;Of course the standard library is augmented by a number of
  third-party libraries. It seems like up until recently it's been
  assumed that you'll use either &lt;tt&gt;apt-get&lt;/tt&gt; as the main way to
  pull these in or run &lt;tt&gt;make install&lt;/tt&gt; from source. As
  I've &lt;a href=&quot;/152&quot;&gt;blogged about recently&lt;/a&gt;, &lt;tt&gt;apt-get&lt;/tt&gt;
  is a bit of a drag for libraries that are obscure or change
  frequently. It looks
  like &lt;a href=&quot;http://oasis.forge.ocamlcore.org/documentation.html&quot;&gt;Oasis&lt;/a&gt;
  is making it easier to build projects
  while &lt;a href=&quot;http://oasis.ocamlcore.org/dev/browse&quot;&gt;ODB&lt;/a&gt; is
  the beginning of a rich library dependency system, though it's
  still got a long way to go. My needs have been simple enough that
  I've been able to stick with the standard library for my current
  project, but it's great to see progress in this direction.&lt;/p&gt;

&lt;p&gt;Anyway, it's always a bit disorienting to toss yourself into a
  new environment like this. But OCaml has proven to be quite a
  treat. I recommend starting
  with &lt;a href=&quot;http://mirror.ocamlcore.org/ocaml-tutorial.org/&quot;&gt;this
  OCaml Tutorial&lt;/a&gt; which also links off to many other helpful
  resources. Have fun!&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt;: As of version 4.5, dmenu has implemented fuzzy
  matching, making erythrina interesting mostly for educational
  purposes.&lt;/p&gt;
    </content>
  </entry>
  
</feed>
