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

  <title><![CDATA[Bathroom Reading Material]]></title>
  <link href="http://blog.raynes.me/atom.xml" rel="self"/>
  <link href="http://blog.raynes.me/"/>
  <updated>2012-12-13T18:15:42-06:00</updated>
  <id>http://blog.raynes.me/</id>
  <author>
    <name><![CDATA[Anthony Grimes]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Moving away from Noir]]></title>
    <link href="http://blog.raynes.me/blog/2012/12/13/moving-away-from-noir/"/>
    <updated>2012-12-13T16:35:00-06:00</updated>
    <id>http://blog.raynes.me/blog/2012/12/13/moving-away-from-noir</id>
    <content type="html"><![CDATA[<p>UPDATE: See the end of this post for a quote from Noir&#8217;s original author, Chris
Granger. Also, here are the commits where I moved to Compojure in refheap: <a href="https://github.com/Raynes/refheap/compare/b943338cc23ff63559fa6190e74f0f39f85badb0...6ab25a24e7e669c7d6482c111bc4a44d5de1997a">https://github.com/Raynes/refheap/compare/b943338cc23ff63559fa6190e74f0f39f85badb0&#8230;6ab25a24e7e669c7d6482c111bc4a44d5de1997a</a></p>

<p>This might be a bit of a sad post for some people, but most will find that it
was simply long overdue and necessary. This post is a formal announcement of
<a href="https://github.com/noir-clojure/noir">Noir</a> being more or less deprecated.</p>

<p>First, let&#8217;s talk about what Noir is. Noir is not really a web framework. What
Noir is is a collection of libraries that most people use when developing a web
application. It was originally designed to make it extremely easy to get new and
potential Clojure developers a website they can look at and use. It served that
purpose really well.</p>

<p>Noir provided things like stateful session, flash, and cookie management, as
well as simple things like a little validation library and a utility for hashing
passwords and such using jcrypt. This what made Noir so popular. People wanted a
batteries included &#8216;framework&#8217; and Noir served that purpose for them.</p>

<p>Under the hood, Noir uses <a href="https://github.com/weavejester/compojure">Compojure</a>
for routing. On top of it, Noir has an
abstraction called &#8216;defpage&#8217; that allows you to create routes statefully and
then call a &#8216;start&#8217; function and have it Just Work. At the time, Compojure
generally required directly calling the jetty adapter and such to get a server
running. You never needed to do that yourself in Noir.</p>

<p>With defpage came some agony as people realized that while Noir&#8217;s abstraction
<em>looks</em> fantastic, you have no control at all over how the routes are defined
and wrapped by middleware. Because of that, Noir was much less flexible than
Compojure. If you&#8217;ve ever tried to have a complex middleware setup on your
routes in Noir (or used <a href="https://github.com/cemerick/friend">Friend</a> with it for that matter), you already know what
I&#8217;m talking about. It is generally impossible to do so much as wrap a specific
set of routes in middleware. This could have been added to Noir, but why bother?
For the longest time, even Chris himself said that Compojure was better for
these situations.</p>

<p>Given all of these realizations, I stopped using Noir quite a while ago. I
recently even moved <a href="https://www.refheap.com">refheap</a> to Compojure.</p>

<p>A big problem that everybody has noticed recently is that Noir is no longer
moving. My buddy Chris has moved on to a bigger part of his life now, working on
Light Table, and does not have time for a lot of his old projects. Noir is used
by quite a few people, so I took on the role of maintainer of the project to the
best of my ability, but I do not know much about Noir&#8217;s internals nor do I have
the time or motivation to work on it. Neither does anyone else.</p>

<p>Chris and I discussed this last night, and we decided that it&#8217;s time to
deprecate Noir and ask that people focus on Compojure instead. The good news is
that you don&#8217;t have to give much of anything up if you move to Compojure! A
while back when I started moving my own sites to Compojure, I took most of the
useful libraries that were embedded in Noir and I split them out into a new
library that you can use from Compojure!
<a href="https://github.com/noir-clojure/lib-noir">lib-noir</a> is the legacy of Noir. The
best thing that came out of it. It has all the useful stateful sessions,
flashes, cookies, as well as the other useful libraries. In fact, the latest
release of Noir depends on this library so if you&#8217;re using it, you&#8217;re already
secretly using lib-noir!</p>

<p>For new websites, please use Compojure and lib-noir. This is pretty much just as
batteries included as Noir itself ever was! You just have to learn how to write
routes with Compojure. It&#8217;s easy and just as concise as it was in Noir. You
don&#8217;t have to use ring-jetty-adapter and stuff, just use the
<a href="http://github.com/weavejester/lein-ring">lein-ring</a> plugin to start your
server. Also, if you took advantage of Noir including hiccup by default, you&#8217;ll
have to have an explicit dependency on it now. No biggy, right? Right!</p>

<p>At this point, I imagine the biggest concern most of you have is what happens to
your existing Noir website. Well, does it work? If so, you should be fine. If
you plan on doing extensive work on Noir websites in the future, I recommend
moving to Compojure as soon as possible.</p>

<p>I will not be maintaining Noir. If there is a serious bug or something that you
would like to fix, open a pull request and ping me on IRC or email or something
and I&#8217;ll merge and release a new version. I won&#8217;t be updating documentation or
anything like that anymore, and I won&#8217;t be adding new features. Beyond that,
you&#8217;re welcome to continue using Noir for the indefinite future, but keep in
mind that as your application ages, it&#8217;ll be much harder to update and
eventually you&#8217;ll end up having to fork Noir to update the versions of libraries
it uses. Better to avoid it altogether.</p>

<p>Understand that there is nothing <em>wrong</em> with Noir, really. We just don&#8217;t think
there is a good reason to keep it around anymore. Compojure and lib-noir are in
such a state that they are a great and potentially better solution. We are
simply moving on.</p>

<p>I will absolutely continue maintaining lib-noir, so if there is anything you&#8217;d
like to add to it, run it by me!</p>

<p>Three cheers for Compojure (and lib-noir)!</p>

<blockquote><p>Just to tack on here: If you&#8217;re using Noir now, don&#8217;t worry, everything is fine - 1.3.0 has been officially released and it&#8217;s not going anywhere. There&#8217;s nothing wrong with what&#8217;s out there now, it&#8217;s just that 1.3.0 will likely be the last release unless someone wanted to keep moving it forward. As time goes on, it likely makes sense to transition, but fortunately transitioning is pretty straightforward and Raynes can point to the commits he used to do that for Refheap. Ultimately, lib-noir + compojure gives a lot more flexibility when it comes to things like middleware at a relatively slight cost to ease of starting. Noir has always used compojure under the covers and so you&#8217;ll find that most everything you&#8217;ve learned so far will map over pretty cleanly.</p><p>When I started the project about two years ago, the Clojure web landscape looked quite a bit different and getting started was an incredibly painful process. Compojure had just split into a bunch of different pieces, there was this new &#8220;ring&#8221; thing, and trying to cobble everything together was a pretty daunting task. Since then, the Clojure web ecosystem has matured quite a bit and lib-noir fits very nicely into the direction the community is ultimately heading in. Noir still serves its purpose as a solid starting point for people who just want to get up and go with Clojure on the web, but I&#8217;ve also come to the conclusion that what we have now is just a baby step toward what we *could* be doing on the web. There are far better solutions waiting out there for Clojure, ones that don&#8217;t just look like a standard http wrapper, and that&#8217;s what we should be ultimately moving toward. We have the fundamental building blocks to build websites, it&#8217;s time we start pushing the state of the art.</p><p>I&#8217;m excited to see what we, the community, come up with.</p><p>Cheers,<br/>Chris.</p><footer><strong>Chris Granger</strong> <cite><a href='https://groups.google.com/forum/#!msg/clj-noir/AbAvQuikjGk/x8lKLKoomM0J'>groups.google.com/forum/#!msg/&hellip;</a></cite></footer></blockquote>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Easy JSON parsing in Haskell with Aeson]]></title>
    <link href="http://blog.raynes.me/blog/2012/11/27/easy-json-parsing-in-haskell-with-aeson/"/>
    <updated>2012-11-27T01:06:00-06:00</updated>
    <id>http://blog.raynes.me/blog/2012/11/27/easy-json-parsing-in-haskell-with-aeson</id>
    <content type="html"><![CDATA[<p>Yeah, I&#8217;ve been using Haskell. Wanna fight about it? Clojure isn&#8217;t everything,
you know? Just most of everything. Something you may not know is that Haskell
was actually my first language, but until recently I had not used it for around
4 years! Haskell was the first language that ever made sense to me, but I hopped
to Clojure pretty quickly after that and there were a lot of awesome things I
missed. In particular, I never really <em>got</em> applicative functors, monads, etc. I
recently got back into Haskell for a delightful change of pace as well as to
learn these things I missed.</p>

<p>Haskell has more or less been a joy to work with. Two areas I found extremely
hard to get into are JSON parsing and HTTP clients. I&#8217;ll be writing a blog post
about the latter soon, but this post is going to focus on JSON parsing.</p>

<p>In Clojure, we can just parse a JSON object into a Clojure map. Hash maps in
Clojure are heterogenous in nature and the keys and values can be any type. We
don&#8217;t have this looseness in Haskell, and the way its type system works means we
have to do things differently.</p>

<p>Usually, you&#8217;re going to want to define some data types that you will populate
with your parsed JSON. Usually, JSON maps to types very easily. First of all,
lets get some JSON to parse. Let&#8217;s use some JSON returned by
<a href="https://www.refheap.com">refheap</a>&#8217;s API:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ http https://www.refheap.com/api/paste/1
</span><span class='line'>HTTP/1.1 200 OK
</span><span class='line'>Connection: keep-alive
</span><span class='line'>Content-Length: 226
</span><span class='line'>Content-Type: application/json;charset=utf-8
</span><span class='line'>Date: Tue, 27 Nov 2012 08:39:26 GMT
</span><span class='line'>Server: Jetty(7.6.1.v20120215)
</span><span class='line'>
</span><span class='line'>{
</span><span class='line'>    "contents": "(begin)", 
</span><span class='line'>    "date": "2012-01-04T01:44:22.964Z", 
</span><span class='line'>    "fork": null, 
</span><span class='line'>    "language": "Clojure", 
</span><span class='line'>    "lines": 1, 
</span><span class='line'>    "paste-id": "1", 
</span><span class='line'>    "private": false, 
</span><span class='line'>    "random-id": "f1fc1181fb294950ca4df7008", 
</span><span class='line'>    "url": "https://www.refheap.com/paste/1", 
</span><span class='line'>    "user": "raynes"
</span><span class='line'>}</span></code></pre></td></tr></table></div></figure>


<p>This is a simple one-level JSON object.</p>

<p>Aeson uses Text instead of regular strings for everything, so to make things <em>a
lot</em> easier on us, we should use a language extension that overloads strings so
that we can just write literal strings and they will work for where Text is
expected:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="cm">{-# LANGUAGE OverloadedStrings #-}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Let&#8217;s go ahead and throw some imports in to start:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">import</span> <span class="nn">Data.Aeson</span> <span class="p">((</span><span class="o">.:</span><span class="p">),</span> <span class="p">(</span><span class="o">.:?</span><span class="p">),</span> <span class="nf">decode</span><span class="p">,</span> <span class="kt">FromJSON</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Applicative</span> <span class="p">((</span><span class="o">&lt;$&gt;</span><span class="p">),</span> <span class="p">(</span><span class="o">&lt;*&gt;</span><span class="p">))</span>
</span><span class='line'><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.ByteString.Lazy.Char8</span> <span class="k">as</span> <span class="n">BS</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now here is the interesting part: we need a type to contain our parsed json
info. Take a look at our JSON. It is&#8230; a paste! A paste is a thing. It is a
thing that always has certain properties. It maps perfectly to a data type:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">data</span> <span class="kt">Paste</span> <span class="ow">=</span> <span class="kt">Paste</span> <span class="p">{</span> <span class="n">getLines</span>    <span class="ow">::</span> <span class="kt">Integer</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getDate</span>     <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getID</span>       <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getLanguage</span> <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getPrivate</span>  <span class="ow">::</span> <span class="kt">Bool</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getURL</span>      <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getUser</span>     <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getBody</span>     <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">}</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Show</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>We defined the <code>Paste</code> type with record syntax to make it easy to get pieces of
it out and to remember what each field is. All of this should be self
explanatory. Note that the <code>user</code> is wrapped in Maybe because anonymous users
can create pastes and in those cases there wouldn&#8217;t be a user at all, so it&#8217;d be
<code>Nothing</code>.</p>

<p>Now here comes the complicated part: we need to define an instance of the
<code>FromJSON</code> typeclass for our new <code>Paste</code> type. If we do this, we will be able to
decode our json directly to our <code>Paste</code> type. Aeson also has a ToJSON typeclass
for if we wanted to write code to convert our <code>Paste</code> type back to JSON, but
we&#8217;re not interested in that. Let&#8217;s write our instance:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">instance</span> <span class="kt">FromJSON</span> <span class="kt">Paste</span> <span class="kr">where</span>
</span><span class='line'>  <span class="n">parseJSON</span> <span class="p">(</span><span class="kt">Object</span> <span class="n">v</span><span class="p">)</span> <span class="ow">=</span>
</span><span class='line'>    <span class="kt">Paste</span> <span class="o">&lt;$&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;lines&quot;</span><span class="p">)</span>     <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;date&quot;</span><span class="p">)</span>      <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;paste-id&quot;</span><span class="p">)</span>  <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;language&quot;</span><span class="p">)</span>  <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;private&quot;</span><span class="p">)</span>   <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;url&quot;</span><span class="p">)</span>       <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:?</span> <span class="s">&quot;user&quot;</span><span class="p">)</span>     <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;contents&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Unfortunately, to really understand this example you need to have knowledge of
applicative functors. Explaining those and the above use of them is waaaaaay
beyond the scope of this post. I&#8217;ll try to make some sense of it though.</p>

<p>Let&#8217;s break things down. The first question you&#8217;ll probably ask is &#8220;What in
tarnations does <code>.:</code> do!?!?!&#8221;. In this case, it looks inside our object for a
JSON key called <code>"lines"</code> and plucks the value out. You&#8217;ll notice that we also
have <code>.:?</code>. The difference between it and <code>.:</code> is that <code>.:?</code> allows for the key
to not exist or be null. It returns a <code>Maybe</code>. Remember that that is precisely
what we need for our possibly non-existing <code>user</code> key.</p>

<p>As far as the applicative functor stuff, you can more or less see what is
happening. Picture it like this: each argument to <code>Paste</code> is in a box. The
operator magic is taking each argument out of its box and applying <code>Paste</code> to
it. The end result is a new <code>Paste</code>. If any of the extractions with <code>.:</code> or
<code>.:?</code> fails, the result will be Nothing and we wont get a paste back. Let&#8217;s try
this out. First define our JSON string. This needs to be a bytestring:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="kt">:</span><span class="n">load</span> <span class="s">&quot;blah.hs&quot;</span>
</span><span class='line'><span class="p">[</span><span class="mi">1</span> <span class="kr">of</span> <span class="mi">1</span><span class="p">]</span> <span class="kt">Compiling</span> <span class="kt">Blah</span>             <span class="p">(</span> <span class="n">blah</span><span class="o">.</span><span class="n">hs</span><span class="p">,</span> <span class="n">interpreted</span> <span class="p">)</span>
</span><span class='line'><span class="kt">Ok</span><span class="p">,</span> <span class="n">modules</span> <span class="n">loaded</span><span class="kt">:</span> <span class="kt">Blah</span><span class="o">.</span>
</span><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="kr">let</span> <span class="n">json</span> <span class="ow">=</span> <span class="kt">BS</span><span class="o">.</span><span class="n">pack</span>
</span><span class='line'><span class="s">&quot;{</span><span class="se">\&quot;</span><span class="s">lines</span><span class="se">\&quot;</span><span class="s">:1,</span><span class="se">\&quot;</span><span class="s">date</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">2012-01-04T01:44:22.964Z</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">paste-id</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">1</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">fork</span><span class="se">\&quot;</span><span class="s">:null,</span><span class="se">\&quot;</span><span class="s">random-id</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">f1fc1181fb294950ca4df7008</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">language</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">Clojure</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">private</span><span class="se">\&quot;</span><span class="s">:false,</span><span class="se">\&quot;</span><span class="s">url</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">https://www.refheap.com/paste/1</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">user</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">raynes</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">contents</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">(begin)</span><span class="se">\&quot;</span><span class="s">}&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Next, we&#8217;ll decode into our Paste type:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="n">decode</span> <span class="n">json</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">Paste</span>
</span><span class='line'><span class="kt">Just</span> <span class="p">(</span><span class="kt">Paste</span> <span class="p">{</span><span class="n">getLines</span> <span class="ow">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">getDate</span> <span class="ow">=</span> <span class="s">&quot;2012-01-04T01:44:22.964Z&quot;</span><span class="p">,</span> <span class="n">getID</span> <span class="ow">=</span> <span class="s">&quot;1&quot;</span><span class="p">,</span>
</span><span class='line'><span class="nf">getLanguage</span> <span class="ow">=</span> <span class="s">&quot;Clojure&quot;</span><span class="p">,</span> <span class="n">getPrivate</span> <span class="ow">=</span> <span class="kt">False</span><span class="p">,</span> <span class="n">getURL</span> <span class="ow">=</span>
</span><span class='line'><span class="s">&quot;https://www.refheap.com/paste/1&quot;</span><span class="p">,</span> <span class="n">getUser</span> <span class="ow">=</span> <span class="kt">Just</span> <span class="s">&quot;raynes&quot;</span><span class="p">,</span> <span class="n">getBody</span> <span class="ow">=</span>
</span><span class='line'><span class="s">&quot;(begin)&quot;</span><span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>


<p>Awesome! As you can see, our result is wrapped in <code>Just</code>. If our JSON didn&#8217;t
contain necessary data or was somehow not well formed, we&#8217;d get back <code>Nothing</code>
instead. Let&#8217;s extract the date from the JSON.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="kr">let</span> <span class="p">(</span><span class="kt">Just</span> <span class="n">x</span><span class="p">)</span> <span class="ow">=</span> <span class="n">decode</span> <span class="n">json</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">Paste</span>
</span><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="n">getDate</span> <span class="n">x</span>
</span><span class='line'><span class="s">&quot;2012-01-04T01:44:22.964Z&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Fantastic. You&#8217;ve just parsed your first JSON in Haskell. Give yourself a pat on
the back.</p>

<p>Let&#8217;s do something a little more complicated now. What if we don&#8217;t want our date
to just be a string? What if we want some sort of date type? What if we want
UTCTime? Let&#8217;s do that. First, we need to import a few more things. Add these
imports:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">import</span> <span class="nn">Data.Time.Format</span>     <span class="p">(</span><span class="nf">parseTime</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Time.Clock</span>      <span class="p">(</span><span class="kt">UTCTime</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">System.Locale</span>        <span class="p">(</span><span class="nf">defaultTimeLocale</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Control.Monad</span>        <span class="p">(</span><span class="nf">liftM</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now, let&#8217;s write a simple little function for parsing dates in the format that
refheap uses. This example is taken from
<a href="https://github.com/Raynes/haskheap">haskheap</a> and you do not need to understand
how it works if you don&#8217;t already know or care, the important part is how we use
it later.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="nf">parseRHTime</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Maybe</span> <span class="kt">UTCTime</span>
</span><span class='line'><span class="nf">parseRHTime</span> <span class="ow">=</span> <span class="n">parseTime</span> <span class="n">defaultTimeLocale</span> <span class="s">&quot;%FT%X%QZ&quot;</span>
</span></code></pre></td></tr></table></div></figure>


<p>This function takes the string date from refheap&#8217;s json and returns <code>Maybe
UTCTime</code>. It is wrapped in <code>Maybe</code> because it could fail if the date isn&#8217;t
well-formed. Now, let&#8217;s change our <code>Paste</code> type to expect <code>UTCTime</code> instead of a
<code>String</code>.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">data</span> <span class="kt">Paste</span> <span class="ow">=</span> <span class="kt">Paste</span> <span class="p">{</span> <span class="n">getLines</span>    <span class="ow">::</span> <span class="kt">Integer</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getDate</span>     <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">UTCTime</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getID</span>       <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getLanguage</span> <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getPrivate</span>  <span class="ow">::</span> <span class="kt">Bool</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getURL</span>      <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getUser</span>     <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getBody</span>     <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">}</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Show</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Excellent. Now all we need to do is change our <code>FromJSON</code> instance.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">instance</span> <span class="kt">FromJSON</span> <span class="kt">Paste</span> <span class="kr">where</span>
</span><span class='line'>  <span class="n">parseJSON</span> <span class="p">(</span><span class="kt">Object</span> <span class="n">v</span><span class="p">)</span> <span class="ow">=</span>
</span><span class='line'>    <span class="kt">Paste</span> <span class="o">&lt;$&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;lines&quot;</span><span class="p">)</span>                  <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="n">liftM</span> <span class="n">parseRHTime</span> <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;date&quot;</span><span class="p">)</span> <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;paste-id&quot;</span><span class="p">)</span>               <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;language&quot;</span><span class="p">)</span>               <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;private&quot;</span><span class="p">)</span>                <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;url&quot;</span><span class="p">)</span>                    <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:?</span> <span class="s">&quot;user&quot;</span><span class="p">)</span>                  <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;contents&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Easy enough, right? The <code>Parser</code> type that <code>.:</code> returns is a monad,
so we just lift our <code>parseRHTime</code> function into it. Now let&#8217;s parse our JSON
again.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="kt">:</span><span class="n">load</span> <span class="s">&quot;blah.hs&quot;</span>
</span><span class='line'><span class="p">[</span><span class="mi">1</span> <span class="kr">of</span> <span class="mi">1</span><span class="p">]</span> <span class="kt">Compiling</span> <span class="kt">Blah</span>             <span class="p">(</span> <span class="n">blah</span><span class="o">.</span><span class="n">hs</span><span class="p">,</span> <span class="n">interpreted</span> <span class="p">)</span>
</span><span class='line'><span class="kt">Ok</span><span class="p">,</span> <span class="n">modules</span> <span class="n">loaded</span><span class="kt">:</span> <span class="kt">Blah</span><span class="o">.</span>
</span><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="kr">let</span> <span class="n">json</span> <span class="ow">=</span> <span class="kt">BS</span><span class="o">.</span><span class="n">pack</span> <span class="s">&quot;{</span><span class="se">\&quot;</span><span class="s">lines</span><span class="se">\&quot;</span><span class="s">:1,</span><span class="se">\&quot;</span><span class="s">date</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">2012-01-04T01:44:22.964Z</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">paste-id</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">1</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">fork</span><span class="se">\&quot;</span><span class="s">:null,</span><span class="se">\&quot;</span><span class="s">random-id</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">f1fc1181fb294950ca4df7008</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">language</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">Clojure</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">private</span><span class="se">\&quot;</span><span class="s">:false,</span><span class="se">\&quot;</span><span class="s">url</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">https://www.refheap.com/paste/1</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">user</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">raynes</span><span class="se">\&quot;</span><span class="s">,</span><span class="se">\&quot;</span><span class="s">contents</span><span class="se">\&quot;</span><span class="s">:</span><span class="se">\&quot;</span><span class="s">(begin)</span><span class="se">\&quot;</span><span class="s">}&quot;</span>
</span><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="kr">let</span> <span class="p">(</span><span class="kt">Just</span> <span class="n">x</span><span class="p">)</span> <span class="ow">=</span> <span class="n">decode</span> <span class="n">json</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">Paste</span>
</span><span class='line'><span class="o">*</span><span class="kt">Blah</span><span class="o">&gt;</span> <span class="n">getDate</span> <span class="n">x</span>
</span><span class='line'><span class="kt">Just</span> <span class="mi">2012</span><span class="o">-</span><span class="mi">01</span><span class="o">-</span><span class="mi">04</span> <span class="mi">01</span><span class="kt">:</span><span class="mi">44</span><span class="kt">:</span><span class="mf">22.964</span> <span class="kt">UTC</span>
</span></code></pre></td></tr></table></div></figure>


<p>Yay! UTCTime! Wee!!!</p>

<p>So yeah, it&#8217;s actually pretty simple. Usage of Aeson isn&#8217;t exactly the most well
documented thing in the world, and if you don&#8217;t at least understand applicative
functors, the examples can be really intimidating. If none of this makes sense,
go dig into <a href="http://learnyouahaskell.com">LYAH</a> for a while. In particular, the
typeclasses through to the applicative functors chapters will help immensely.</p>

<p>Okay, so the above decode-straight-to-a-type sort of thing is the most common
way of working with JSON. But you don&#8217;t always need or want to define a new type
just to grab a bit of information from some JSON. What if our code only cared
about the number of lines in a paste? We could write a <code>Lines</code> data type and
then an instance, but is it really worth it? We can just extract that piece
out. Let&#8217;s rejigger our imports again:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.HashMap.Strict</span> <span class="k">as</span> <span class="n">HM</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Attoparsec.Number</span> <span class="p">(</span><span class="kt">Number</span><span class="p">(</span><span class="o">..</span><span class="p">))</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now, let&#8217;s write a function to extract the number of lines from the json.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="nf">getRHLines</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Integer</span>
</span><span class='line'><span class="nf">getRHLines</span> <span class="n">json</span> <span class="ow">=</span>
</span><span class='line'>  <span class="kr">case</span> <span class="kt">HM</span><span class="o">.</span><span class="n">lookup</span> <span class="s">&quot;lines&quot;</span> <span class="n">hm</span> <span class="kr">of</span>
</span><span class='line'>    <span class="kt">Just</span> <span class="p">(</span><span class="kt">Number</span> <span class="p">(</span><span class="kt">I</span> <span class="n">n</span><span class="p">))</span> <span class="ow">-&gt;</span> <span class="n">n</span>
</span><span class='line'>    <span class="kt">Nothing</span>             <span class="ow">-&gt;</span> <span class="ne">error</span> <span class="s">&quot;Y U NO HAS NUMBER?&quot;</span>
</span><span class='line'>  <span class="kr">where</span> <span class="p">(</span><span class="kt">Just</span> <span class="p">(</span><span class="kt">Object</span> <span class="n">hm</span><span class="p">))</span> <span class="ow">=</span> <span class="n">decode</span> <span class="p">(</span><span class="kt">BS</span><span class="o">.</span><span class="n">pack</span> <span class="n">json</span><span class="p">)</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">Value</span>
</span></code></pre></td></tr></table></div></figure>


<p>This isn&#8217;t nearly as complicated as it looks. It&#8217;s mostly just a lot of
destructuring. The meat of the example is that we are decoding the json to
<code>Maybe Value</code> instead of our own type. Our JSON is a big JSON object, and aeson
represents this as a hashmap. We can just look the lines key up in it! Note that
the <code>Number</code> type here is a wrapper around the <code>Number</code> type from attoparsec. It
is just a fancy type for representing different numbers with decent
precision. In this case, it represents an Integer (<code>I</code>), so we&#8217;re destructuring
on that constructor.</p>

<p>Well, that&#8217;s about all there is to it. There is also a funky trick for
automatically defining instances, but I&#8217;m not a big fan of that and it is
outside the scope of this post.</p>

<p>Here is the completed code:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
</pre></td><td class='code'><pre><code class='haskell'><span class='line'><span class="cm">{-# LANGUAGE OverloadedStrings #-}</span>
</span><span class='line'><span class="kr">module</span> <span class="nn">Blah</span> <span class="kr">where</span>
</span><span class='line'>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Aeson</span>             <span class="p">((</span><span class="o">.:</span><span class="p">),</span> <span class="p">(</span><span class="o">.:?</span><span class="p">),</span> <span class="nf">decode</span><span class="p">,</span> <span class="kt">FromJSON</span><span class="p">(</span><span class="o">..</span><span class="p">),</span> <span class="kt">Value</span><span class="p">(</span><span class="o">..</span><span class="p">))</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Control.Applicative</span>    <span class="p">((</span><span class="o">&lt;$&gt;</span><span class="p">),</span> <span class="p">(</span><span class="o">&lt;*&gt;</span><span class="p">))</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Time.Format</span>       <span class="p">(</span><span class="nf">parseTime</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Time.Clock</span>        <span class="p">(</span><span class="kt">UTCTime</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">System.Locale</span>          <span class="p">(</span><span class="nf">defaultTimeLocale</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Control.Monad</span>          <span class="p">(</span><span class="nf">liftM</span><span class="p">)</span>
</span><span class='line'><span class="kr">import</span> <span class="nn">Data.Attoparsec.Number</span> <span class="p">(</span><span class="kt">Number</span><span class="p">(</span><span class="o">..</span><span class="p">))</span>
</span><span class='line'><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.HashMap.Strict</span> <span class="k">as</span> <span class="n">HM</span>
</span><span class='line'><span class="kr">import</span> <span class="k">qualified</span> <span class="nn">Data.ByteString.Lazy.Char8</span> <span class="k">as</span> <span class="n">BS</span>
</span><span class='line'>
</span><span class='line'><span class="nf">parseRHTime</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Maybe</span> <span class="kt">UTCTime</span>
</span><span class='line'><span class="nf">parseRHTime</span> <span class="ow">=</span> <span class="n">parseTime</span> <span class="n">defaultTimeLocale</span> <span class="s">&quot;%FT%X%QZ&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="kr">data</span> <span class="kt">Paste</span> <span class="ow">=</span> <span class="kt">Paste</span> <span class="p">{</span> <span class="n">getLines</span>    <span class="ow">::</span> <span class="kt">Integer</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getDate</span>     <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">UTCTime</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getID</span>       <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getLanguage</span> <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getPrivate</span>  <span class="ow">::</span> <span class="kt">Bool</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getURL</span>      <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getUser</span>     <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">,</span> <span class="n">getBody</span>     <span class="ow">::</span> <span class="kt">String</span>
</span><span class='line'>                   <span class="p">}</span> <span class="kr">deriving</span> <span class="p">(</span><span class="kt">Show</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="kr">instance</span> <span class="kt">FromJSON</span> <span class="kt">Paste</span> <span class="kr">where</span>
</span><span class='line'>  <span class="n">parseJSON</span> <span class="p">(</span><span class="kt">Object</span> <span class="n">v</span><span class="p">)</span> <span class="ow">=</span>
</span><span class='line'>    <span class="kt">Paste</span> <span class="o">&lt;$&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;lines&quot;</span><span class="p">)</span>                  <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="n">liftM</span> <span class="n">parseRHTime</span> <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;date&quot;</span><span class="p">)</span> <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;paste-id&quot;</span><span class="p">)</span>               <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;language&quot;</span><span class="p">)</span>               <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;private&quot;</span><span class="p">)</span>                <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;url&quot;</span><span class="p">)</span>                    <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:?</span> <span class="s">&quot;user&quot;</span><span class="p">)</span>                  <span class="o">&lt;*&gt;</span>
</span><span class='line'>    <span class="p">(</span><span class="n">v</span> <span class="o">.:</span> <span class="s">&quot;contents&quot;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="nf">getRHLines</span> <span class="ow">::</span> <span class="kt">String</span> <span class="ow">-&gt;</span> <span class="kt">Integer</span>
</span><span class='line'><span class="nf">getRHLines</span> <span class="n">json</span> <span class="ow">=</span>
</span><span class='line'>  <span class="kr">case</span> <span class="kt">HM</span><span class="o">.</span><span class="n">lookup</span> <span class="s">&quot;lines&quot;</span> <span class="n">hm</span> <span class="kr">of</span>
</span><span class='line'>    <span class="kt">Just</span> <span class="p">(</span><span class="kt">Number</span> <span class="p">(</span><span class="kt">I</span> <span class="n">n</span><span class="p">))</span> <span class="ow">-&gt;</span> <span class="n">n</span>
</span><span class='line'>    <span class="kt">Nothing</span>             <span class="ow">-&gt;</span> <span class="ne">error</span> <span class="s">&quot;Y U NO HAS NUMBER?&quot;</span>
</span><span class='line'>  <span class="kr">where</span> <span class="p">(</span><span class="kt">Just</span> <span class="p">(</span><span class="kt">Object</span> <span class="n">hm</span><span class="p">))</span> <span class="ow">=</span> <span class="n">decode</span> <span class="p">(</span><span class="kt">BS</span><span class="o">.</span><span class="n">pack</span> <span class="n">json</span><span class="p">)</span> <span class="ow">::</span> <span class="kt">Maybe</span> <span class="kt">Value</span>
</span></code></pre></td></tr></table></div></figure>


<p>Not half bad for 44 lines, eh? It isn&#8217;t nowhere near as intuitive as doing the
same things in Clojure is, but that&#8217;s because Clojure doesn&#8217;t have Haskell&#8217;s
type system. JSON parsing in Haskell is necessarily complicated, but Aeson does
an excellent job of making it not-that-hard. What Aeson really needs is some
serious beginner-oriented documentation.</p>

<p>This is the first Haskell blog post I&#8217;ve ever written and is based on the first
Haskell code I&#8217;ve written in about 4 years. Please be gentle. If anything is
wrong or can be improved, please let me know via email or a comment! I&#8217;ll be
writing a blog post about http-conduit soon because I also found it a huge pain
in the ass to work with. Stay tuned.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Help fund my trip to the conj]]></title>
    <link href="http://blog.raynes.me/blog/2012/10/12/help-fund-my-trip-to-the-conj/"/>
    <updated>2012-10-12T15:45:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2012/10/12/help-fund-my-trip-to-the-conj</id>
    <content type="html"><![CDATA[<p>!!! GOAL REACHED! WOOHOO! LOOK FOR A FOLLOW-UP BLOG POST SOON WITH A LIST OF
-DONORS !!!</p>

<p>Hello there boys and girls of all ages, shapes, and sizes. It&#8217;s your old friend
Anthony Grimes (Raynes) here with an appeal.</p>

<p>You see, I didn&#8217;t plan on going to the <a href="http://clojure-conj.org">Clojure Conj</a>
conference this year. A sad decision it was, I knew I wouldn&#8217;t be able to pay
for a ticket as well as airfare and a room, since I currently only work part
time as an intern with <a href="http://geni.com">Geni</a>. I could have submitted a talk,
but I decided against it so that I could focus on things other than slideshows
for a while.</p>

<p>But hey, I should have known that wasn&#8217;t the end of it. When has the Clojure
community ever let me miss out on something? Never is when. This time is
certainly no different. I wandered into my email account about a week ago and
there sat an email from Lynn Grogan. She was asking if I was going to the
Conj. She said that &#8220;at least one person wants to donate their ticket to
you&#8221;. As spectacularly impossible and amazing this was, I had to email her back
saying that I still wouldn&#8217;t be able to cover airfare and a hotel room on such
short notice.</p>

<p>Less than 30 seconds later, good ol&#8217; Chas Emerick pings me on IRC and we start
jotting out how I can get there. I came to learn that my good friend Carin Meier
(an avid 4Clojure contributor and all around good person) was the person who was
donating her ticket. Unfortunately she is unable to go to the Conj this year and
didn&#8217;t want her ticket to go to waste. She thought of me! Everybody give her a
round of applause!</p>

<p>So Chas and I discussed things for a while and we decided that I should follow
in
<a href="http://code.google.com/p/counterclockwise/wiki/ConjTripFunding">Laurent Petit</a>&#8217;s
footsteps and hold a fundraiser of my own. If you&#8217;ve been around Clojure for a
couple of years (or at least read hacker news), you will probably remember that
this isn&#8217;t the first time the Clojure community has helped me exceed my own
limitations and do something great. In fact, my first plane ride was on a
<a href="http://cemerick.com/2010/09/10/a-clojure-scholarship-lets-send-raynes-to-the-conj/">community funded trip to the first Conj when I was 16</a>. Needless
to say, those were some of the most memorable and amazing days of my life.</p>

<p>In summary, I only found out a few days ago that I now have a free ticket to the
conj. I had no intentions of going so I have no money at all saved up for the
trip. Even more problematic is that plane tickets are now higher than
usual. Here is what I&#8217;m looking at:</p>

<ul>
<li>Hotel room at the conference booked from the 14th to the 18th of November at
$134 a night: $536</li>
<li>Airfare roundtrip from Birmingham, AL to Raleigh, NC at $368: $368</li>
<li>I&#8217;m going to raise the ceiling a bit to help cover a ticket for my mother (who
really enjoys these trips), food and drink, and a little emergency money just
in case. $400</li>
</ul>


<p>Grand total: $1304. I have set the goal to an even $1300.</p>

<p>Your next question may very well be &#8220;Who the hell are you and why should I
donate?&#8221;. My name is Anthony Grimes. I&#8217;ve been around the Clojure community
since I was around 15 years old and have developed a close relationship with its
members because of it. I am the author of numerous projects, including some
well-known ones such as</p>

<ul>
<li><a href="http://tryclj.com">TryClojure</a>, a sandboxed online REPL + mini tutorial for
new Clojurians.</li>
<li><a href="https://www.refheap.com">Refheap</a>, a pastebin written in Clojure that aspires
to exceed the standard set by gist, with a focus on usability for Clojure
users.</li>
<li><a href="https://github.com/flatland/clojail">Clojail</a>, a Clojure sandboxing library
that powers 4Clojure and TryClojure.</li>
<li><a href="https://github.com/flatland/lazybot">lazybot</a>, an IRC bot written in Clojure
with Clojure evaluation and a host of other features. It is no doubt the best
bot evars.</li>
</ul>


<p>You can see the rest of my projects and activity on
<a href="https://github.com/Raynes">github</a>.</p>

<p>In addition to my own projects, I am an avid proponent and contributor to</p>

<ul>
<li>The <a href="https://github.com/technomancy/leiningen">leiningen</a> build tool, having
contributed some built-in commands and numerous plugins, including
<a href="https://github.com/Raynes/lein-newnew">lein-newnew</a></li>
<li><a href="http://4clojure.com">4Clojure</a>, an excellent learning tool for Clojure
developers that poses problems and asks you for solutions.</li>
<li>The <a href="https://github.com/flatland">flatland organization</a>, a whole host of
libraries and tools written by a group of friends and colleagues at Geni.</li>
</ul>


<p>I
<a href="http://blip.tv/clojure/anthony-grimes-clojail-life-in-the-clojure-prison-5919720">spoke about clojail</a>
at last year&#8217;s Conj.</p>

<p>At this point in time, I am 18 years old and I live in a small town in Alabama
called Eldridge.</p>

<p>So that&#8217;s me and this is my appeal. This trip would be an excellent opportunity
to me to learn about and experience new things as well as keep in touch with the
community and discuss projects/ideas. Any donations are appreciated and if you
leave your name with your donation I will list it in a follow-up blog post and
personally dance for you at the Conj<sup>1</sup>.</p>

<p>Paypal doesn&#8217;t really have anything in the way of progress bars and the like, so
I&#8217;m going to update this blog post manually. Don&#8217;t expect the total to rise
immediately after you donate. Also keep in mind that the paypal account is my
mother&#8217;s (I don&#8217;t have a personal account because I rarely use paypal), so the
name listed is Connie Simpson.</p>

<p>Button removed! Goal reached!</p>

<p>Goal reached at 5:30PM: $1320.90</p>

<p><sub>1: No, I wont.</sub></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Meet Clojure update: I don't have time to write this blog post]]></title>
    <link href="http://blog.raynes.me/blog/2012/08/05/meet-clojure-update-i-dont-have-time-to-write-this-blog-post/"/>
    <updated>2012-08-05T15:40:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2012/08/05/meet-clojure-update-i-dont-have-time-to-write-this-blog-post</id>
    <content type="html"><![CDATA[<p>It has been quite a while since I updated everyone on the progress of my book, Meet Clojure, and
several people have asked about it over the past 2 weeks, so I think now is the time for an
explanation.</p>

<p>As everyone knows, I&#8217;ve been working on Meet Clojure for over a year. Unfortunately, for the vast
majority of that time I haven&#8217;t actually had the time to work on it. Not to say I didn&#8217;t ever get
anything done. To the contrary, there are several chapters written. But in the world of book
writing, there is a huge difference between a chapter being written and a chapter being finished.
I am not a perfectionist, but this book is meant to be good, so I&#8217;m generally unsatisfied with
everything I&#8217;ve written and will eventually need to rewrite most of it.</p>

<p>But what needs to be done isn&#8217;t the problem at all. The concern is what is keeping it from being
done. When I got my publishing deal with No Starch was right about the time that I got a job at
Geni. For a while there I was able to work just fine, have a relatively decent life on the side,
keep up with personal projects, and spend enough time on the book to keep things moving along
(slowly but surely).</p>

<p>My biggest problem is arguably my biggest asset: I try to do too many things at once. Go look at
all the stuff on my Github page. I overwhelm myself before I realize I&#8217;m doing it. I have to fight
with myself to figure out ways to focus on specific things for specific amounts of time and try to
not neglect the other things. Refheap is an excellent example. There is <em>so</em> much I want to do with
it but it progresses slowly because if I focused on it, other things would suffer.</p>

<p>Aside from all of this, I want to be able to focus on what I have to do where I work as well. If I&#8217;m
stressed out, I&#8217;m not doing my coworkers any good. I wont get good code written and I wont be able to
keep the standards that got me the job in the first place.</p>

<p>And that&#8217;s the goal behind it all: stress management. How much can I do and how often can I do it
without being exhausted, stressed out, and worthless to everybody? I spent a lot of time thinking
about it and a lot of time talking to close friends and trusted colleagues, and I&#8217;ve decided that,
for <strong>right now</strong>, the book is too much for me to handle.</p>

<h2>Okay, stop panicking. Calm down. Deep breaths. Let me explain myself.</h2>

<p>There are a <em>lot</em> of Clojure books around. The latest one <a href="http://www.clojurebook.com/">Clojure Programming</a>
by my good friend Chas Emerick is a fantastic one. I talked to my buddy (and technical reviewer) Chris Granger
about this before I made any decisions, and one thing he said that helped me make the decision was:
&#8220;You can contribute to the Clojure community better without the book right now.&#8221;. He is correct.
Without the book, I have time to work on my various libraries and tools and help out in whatever
capacity is needed. We have plenty of good books, and we&#8217;re not going to suffer any dramatic losses
if mine isn&#8217;t completed right now.</p>

<h2>I still intend to write this book</h2>

<p>This is the most important part of this blog post. I am <em>not</em> under any circumstances giving up the book.
I still think the book will be an important addition to the Clojure bookshelf. I simply do not have the
time or energy to make it happen <em>at the moment</em>.</p>

<p>My plan is just to wait. I don&#8217;t know exactly how long, but I&#8217;m just going to wait for a crack. A point
where my life is not so utterly insane. I&#8217;m going to work on my personal projects, do a great job at
Geni, and spend as much spare time as possible with my mother and my aunt, because I&#8217;m likely going to
be moving far, far away in the not so very distant future. These are the things that are most important
to me at the moment.</p>

<h2>Future plans</h2>

<p>The first thing I did was discuss these things with my amazing publisher, No Starch Press. I have the
most amazing editor ever in Keith Fancher, and he is extremely open to what I have to say and how I
feel. He was entirely sympathetic. I sent the email with the expectation that they would tell me to
bugger off and write the book myself, but that&#8217;s the precise opposite of what he said. I certainly
plan to pick the book back up when life allows (when I can do a good job on it), so he proposed that
we set a date where we can touch base in the future. I proposed 6 months and he agreed.</p>

<p>So rest assured everyone, the book is going to happen. I just can&#8217;t tell you when. For the time being,
I am not actively working on it. When I&#8217;m able to pick it back up again, I&#8217;ll be sure to let everyone
know.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Defining Life Based On Who You're Stuck In A Room With]]></title>
    <link href="http://blog.raynes.me/blog/2012/04/23/defining-life-based-on-who-youre-stuck-in-a-room-with/"/>
    <updated>2012-04-23T02:27:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2012/04/23/defining-life-based-on-who-youre-stuck-in-a-room-with</id>
    <content type="html"><![CDATA[<p>I turned 18 in February of this year. I&#8217;ve long known that hitting that
milestone would mean a lot major life changes in quick succession, and I
have prepared to be overwhelmed. I&#8217;ve been told that being overwhelmed
is the only logical human thing to experience when these sorts of things
begin to happen. I&#8217;ve got to say, it&#8217;s one hell of a ride, but it&#8217;s one
I&#8217;m very happy to be taking.</p>

<h2>Work</h2>

<p>I&#8217;ve been working with a wonderful company called
<a href="http://geni.com">Geni</a> for the past year. Geni is a genealogy
website dedicated to building a family tree of the whole world. You make
an account and create your family tree.</p>

<p>Around two years ago, I was lucky enough to meet and become good friends
with Justin Balthrop and Lance Bradley, both engineers at Geni. They had
just created the cake build tool and I was one of the first users. The
first experience I remember with these guys was me arguing with them
over a feature cake had that I did not like, which was that it would
start a swank server by default whenever you ran a command. I can&#8217;t
remember the details of why this was a problem with me, but the end
result was that they made the feature more configurable and off by
default.</p>

<p>Days go by, we become friends. We met in person for the first time at
the first Clojure Conj conference in late 2011. We spent the entire
conference together. We had a lot of fun.</p>

<p>Eventually, Lance and Justin began talking about the possibility of me
working at Geni as an intern. I think I was still 16 when I had my phone
interview with the then-CTO, Amos. Several months passed while details
were worked out. Amos left the company and Justin was promoted to CTO,
which gave him more wiggle room for bringing me on as an intern quickly.
I started in March of last year.</p>

<p>Oh my, what a wonderful year. 17 years old, working part time with
Clojure from my own home all the way across the country. Even making
money! Lance and Justin have taught me so much and given me so much
experience. They&#8217;ve helped build my skill and my confidence in my skill,
and I don&#8217;t think I&#8217;d be even remotely the man I am right now without
their ongoing influence. I don&#8217;t think they&#8217;ll ever know what they&#8217;ve
done and continue to do for me.</p>

<h2>Turning 18</h2>

<p>Turning 18 was always an important milestone for my Geni work. As long
as I was a minor and still in high school, I could only work part time
and the legality of things would be complicated.</p>

<p>Our plan was to fly me out to Los Angeles when I turned 18. I would work
in the office for a week face to face with my coworkers and experience
LA. A few weeks before I was meant to make this trip, I freaked out for
a few days. Moving away from home is the expected end result of all of
this. Unfortunately, there are circumstances preventing me from relocating
my family at this point in time, and thus this is something that I would
have to do alone.</p>

<p>Needless to say, this scared the hell out of me. So much so that I
talked to Lance and Justin and pleaded for a way to continue working
there and not have to uproot. Justin encouraged me to relax and just
come out there for the trip and talk again after LA was no longer an
imaginary place. His and Lance&#8217;s calm regarding this matter along with
encouragement from my family to make this happen calmed me down
significantly. I still wasn&#8217;t sure about moving, but the possibility
stopped being ridiculous and began to make sense.</p>

<h2>Los Angeles</h2>

<p>The plan became a reality last week. I stepped on my flight on the
morning of Friday the 13th. This was the first flight I had ever taken
by myself, and it would lead me to a place farther away from home than I
had ever been before.</p>

<p>I had a connection in Nashville where I got on a plane to Los Angeles.
After this uneventful 4 hour flight, I found myself on an escalator down
to a man waiting for me with my name on a card. This man drove me to the
studio where I was to stay until the 21st. What makes this drive notable
is that my best friend Alan (also an engineer at Geni) sent me a text
message saying &#8220;Welcome to LA!&#8221;. I smiled so hard that I&#8217;m surprised my
teeth didn&#8217;t fall out. My entire experience was braced on this text
message. Los Angeles stopped being this scary place where I would be
alone. Much to the contrary, I was surrounded by friends; the greatest
people I&#8217;ve ever had the privilege of knowing. And they all wanted me
there. They were all waiting for me to arrive. If you&#8217;ve never
experienced this, you&#8217;re missing out.</p>

<p>I was picked up shortly after arriving by Lance and carted to the Geni
office on Sunset Boulevard for the first time. They had set me up a desk
and an amazing Aeron chair. Lance gave me a tour of the office and then
he, Justin, Alan, and I all headed across the street to BOA Steakhouse
for a fantastic dinner.</p>

<p>This was the beginning of a whirlwind weekend where Lance showed me the
most amazing things I&#8217;ve ever seen. He gave me the tour of a lifetime
and conversation that I will never forget. Alan came along with us for a
lot of the weekend activities. Notably, Lance showed me Hollywood, Santa
Monica, and Venice. I had never seen the ocean before, so it was mind
blowing. We all took our shoes off and stood in the cold waters of
pacific tide at Venice beach. I&#8217;ve never had so much fun in my life. Not
for one second did I think of home, leaving my family, uprooting,
changing. All that mattered was how I felt, and I felt amazing.</p>

<p>We did so many things that weekend that I couldn&#8217;t bring them all to
mind in order to list them even if I tried. Notably, we saw a wonderful
movie called The Chronicle and Lance and Alan took me to my first 3D movie
(Wrath of the Titans) which I thoroughly enjoyed.</p>

<p>Monday begins my first 40 hour week at Geni, working in the office face
to face with my coworkers. I felt at home the moment I plugged my laptop
into the monitor on my desk. The conversations between my coworkers are
smart and well worth listening to, and we have plenty of fun in the
office as well. Light Table was kickstarting at the time, so we had lots
of conversation about that and we followed developments closely.</p>

<p>During the week, Lance and I would sometimes do stuff after work. Lance
took me to the hills one night and showed me the most amazing view of
the city from above. The lights go on forever and the cars appear to be
infinite. It is like an ocean on land, and it surrounds you on every
side. It is inspiring and overwhelming in its beauty and is one of my
highlights from the week.</p>

<p>I got a lot of work done during the week. I had never bought into the
whole you-gain-a-lot-by-working-face-to-face thing, but I have to say,
I believe it now. It is a lot easier to get things done and to
concentrate when you&#8217;re in an office setting. If you need something from
somebody, you find them and talk to them. There are few distractions.
I highly value an office setting now and I&#8217;m excited at the prospect of
doing it every day.</p>

<p>By Thursday, two nights before the morning I was to leave, I found myself
in a position that I never expected myself to be in: I did not want to go
home at all. I was enjoying it so much that the fact that I had to go home
at the end of the week had been entirely an afterthought. It was saddening
when the time finally came.</p>

<p>On Thursday, Lance and I left work early and drove to Malibu. Lance has
suggested that Venice beach was not a good first beach experience. I
didn&#8217;t know what he meant until I saw the beaches of Malibu. They were
so utterly majestic! The first thing I noticed was that I could, for the
first time, noticeably smell the ocean air. We went below Point Dume and
climbed over rocks to tide pools. The scenery took my breath away.</p>

<p>Earlier in the day, Justin told me he wanted to talk about my trip and
my plans for the future. We went to a private room and talked. He was
concerned about putting pressure on me. He asked me how I liked it there
and if I had decided whether or not I wanted to come work at Geni full
time and live in LA. I was telling him yes in the blink of an eye.</p>

<p>Things are set in motion now. I get my diploma on the 25th of this month
(April), and Geni will be able to make me a full time offer. In the next
couple of months, I&#8217;ll move to Los Angeles and begin a life on my own.</p>

<h2>Stuck In A Room</h2>

<p>There is an episode of House where House talks with a patient (who had
been raped and was pregnant) who he is reluctant to treat. There is a
part where the girl says something along the lines of &#8220;All life is is a
series of rooms, and who you&#8217;re stuck in those rooms with adds up to
what your life is.&#8221;. This is what comes to mind when I think of this
trip and my future.</p>

<p>Moving to LA, I will not be alone. In fact, I&#8217;ll be stuck in a series of
rooms. The important thing is that there is nobody in this world who I
would rather be stuck in those rooms with. I&#8217;ve got the best friends in
the world, and I have the luxury of them also being my coworkers. I&#8217;ve
never wanted to be stuck in a room so much in my life.</p>

<p>My friends have given me the best and most important week of my life. It
was a life-changing trip that I will never forget. It has and will shape
my life and future. A follower on twitter mentioned that it is amazing
when things click, and I have to say that I agree.</p>

<p>I&#8217;m very excited about my future. My life is about to change radically,
and I&#8217;m okay with it. I have choices and options I&#8217;ve never had before,
and I see my path in front of me. It is going to be one hell of a ride,
but it is a ride that I&#8217;m happy to take. Lance, Justin, Alan, thanks for
making this a reality. Thanks for making things easy. Thanks for giving
me an amazing new room to get stuck in.</p>

<p>Thanks for reading.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[I Code With Things and Stuff]]></title>
    <link href="http://blog.raynes.me/blog/2012/02/01/i-code-with-things-and-stuff/"/>
    <updated>2012-02-01T11:21:00-06:00</updated>
    <id>http://blog.raynes.me/blog/2012/02/01/i-code-with-things-and-stuff</id>
    <content type="html"><![CDATA[<p>A lot of radical changes in my development environment resulted in
someone asking me if I&#8217;d write a blog post about it. I was already
considering doing that, and the interest of at least one person is
motivation enough to actually do it.</p>

<h2>Background</h2>

<p>I used Emacs for around two years. It started back in my Haskell days.
You know how Lispers love Emacs because it has SLIME? Haskellers seem to
love Emacs because it can handle Haskell&#8217;s complex indentation rules
(rules that aren&#8217;t really rules but still shouldn&#8217;t be broken).</p>

<p>When I started using Clojure, I was lucky enough to already have some
Emacs under my belt and so I continued using it.</p>

<h2>Emacs</h2>

<p>Oh how I love Emacs. When I used Emacs, I even used it as my IRC client
via ERC. ido-mode was my lord and savior and a day without it was a day
spent in the deepest bowels of hell.</p>

<p>When you use Emacs for a language like Clojure, you tend to get smug
towards people who use other editors (particularly Vim) because you have
SLIME, the Superior Lisp Interaction Mode Environment Blah Whatever. It
is a fantastic development environment for Lisp languages (including
Clojure) that provides debugging (I don&#8217;t think this actually applies to
Clojure yet), completion, interactive development inside of Emacs, and
all sorts of other stuff that makes birds sing and babies be born.</p>

<p>Though I used and loved Emacs, I almost never used SLIME. Don&#8217;t get me
wrong, I respect and understand why it is so important to people, I just
never really used it. If I did use it, it was because having a REPL
inside of my Emacs window was more convenient than switching to a
terminal window to enter some code. Plus, when you use SLIME (or
inferior-lisp which is a completely different and simpler thing), you
can send code from one buffer to another without copying and pasting. It
makes things really quick and easy. That&#8217;s all I ever used SLIME for.</p>

<p>The greatest thing about Emacs is just how much you can do
with it. The fact that SLIME is possible in the first place and that it
can work out so well is spectacular. Emacs is built around a Lisp called
elisp. As such, Emacs is probably the most extensible editor that ever
has been or ever will be. You can make Vim do magic tricks, but Emacs
really can pull a rabbit out of a hat.</p>

<p>And that&#8217;s what has always bothered me. When people talk about Vim vs
Emacs they end up focusing on the least important things. You can&#8217;t even
have a discussion about the merit of one or the other without a flurry
of childish remarks. Sure, Emacs is an operating system. <strong>That&#8217;s the
point.</strong> Emacs is an execution environment for a Lisp. It is <em>designed</em>
for you to be able to do anything with it. That isn&#8217;t a flaw. It is
insignificantly bigger than Vim and insignificantly more
resource-intensive because it is <em>supposed</em> to be.</p>

<p>So, what about important things? Key chords versus colon commands?
Sure, this one is relevant. A lot of people dismiss Emacs because of it
being driven by keyboard shortcuts off of a number of modifier keys. In
particular, a lot of people complain of &#8220;Emacs pinkie&#8221; because of the
position of the control key and the number of times an Emacs user has to
use it to get anything done. I can understand this complaint, though
I&#8217;ve never experienced problems related to it. As a matter of fact, I&#8217;ve
only had control mapped to caps lock for one year. My work sent me this
laptop with it already remapped and I decided to leave it like that. I
got used to it so it wasn&#8217;t in my interest to switch it back. I&#8217;m still
not sure I&#8217;ve gained anything by having it moved there.</p>

<p>But yes, key chords vs colon commands does matter to some people and I
can understand why.</p>

<h2>Experimentation</h2>

<p>I like trying new things in my development environment. New editors, new
IRC clients, new color schemes, new terminal emulators, etc. I get
bored. I recently started playing around with text editors with no real
intention of moving away from Emacs.</p>

<p>First of all, a friend and I have been working on
<a href="https://refheap.com">RefHeap</a>, a pastebin with an API. The first thing
I did was write an <a href="https://github.com/Raynes/refheap.el">Emacs plugin</a>
(library?) for it. A lot of people wont even consider a pastebin unless
it has support for their editor, so I figured hey, I should just use as
many popular editors as possible and write a plugin for all of them. Next
stop: Sublime Text 2.</p>

<h3>Sublime Text 2</h3>

<p>At first glance, this editor is beautiful. It has the prettiest indent
guides I&#8217;ve ever seen and lovely syntax highlighting. It has an
excellent set of themes including the full set of tomorrow night themes
which I used the most at the time. It is also a highly extensible
editor. Plugins are written in Python and aren&#8217;t very difficult to write
if you can read an existing plugin or manage to find up-to-date
information about them.</p>

<p>Unfortunately, it just doesn&#8217;t seem to have a very good editor. Trying
to edit Lisp code in it is so painful that it is nearly impossible. It
doesn&#8217;t know how to auto-indent even remotely properly and the Clojure
syntax file that comes with ST2 breaks on regexes with escaped double
quotes. I fixed that with a guy&#8217;s recommendation from twitter, but it
was a bandaid on an infected wound.</p>

<p>The thing that bothers me the most about Sublime Text 2 is the way you
open files with it. In Emacs and Vim, you can execute a command and type
a path to a file anywhere on the harddrive with completion and
interactive features (see ido-mode for Emacs and wildmenu for Vim). In
ST2, you have two options for opening files:</p>

<ul>
<li>You use the operating system&#8217;s Open File dialog.</li>
<li>You use the goto anything feature which lets you type a path and gives
you fantastic fuzzy completion, but only works relative to your
current directory, meaning you can&#8217;t be in <code>/Users/anthony/foo</code> and open
a file in <code>/Users/anthony/</code> like this.</li>
</ul>


<p>Also, ST2&#8217;s Open File dialog is completely separate from its Open Folder
function. If you want to open a folder instead of a file, you have to
use that dialog. Oh, and there is no keyboard shortcut for this.</p>

<p>All in all, the operating system&#8217;s open file dialog is a really weak way
for a programmer to navigate the file system to open the file he needs.
There is nothing in ST2 stopping this from being implemented AFAIK, but
I guess I&#8217;m the only one who thinks what is done now is completely
insane. Separating opening files and opening folders and forcing it to
be done via a OS dialog is just not something I&#8217;m prepared to live with.</p>

<p>I actually used Sublime Text 2 for about a day and a half until I really
had to edit some Clojure code. There was just nothing to be done. I like
Sublime Text 2, but it is pretty worthless for Clojure. Since RefHeap&#8217;s
primary market is Clojure programmers and Sublime Text 2 users in the
community are a very, very small minority, I just decided to call it
quits on that. I will absolutely revisit this (it is really easy to
write a RefHeap plugin in Python) if ST2 ever becomes better for Clojure
and I can tolerate it long enough to test such a plugin.</p>

<p>I&#8217;d like to add that my opinion of ST2 is <em>entirely</em> based around its
support for Clojure. From what I saw, it is an <strong>excellent</strong> editor for
other languages such as Python and Ruby. This makes perfect sense
because the author and majority of contributors (to the parts that you
can contribute to) use these languages and don&#8217;t use Clojure. I expect
that the Clojure portion of things will get more love as it gets more
popular in the Clojure community.</p>

<h3>Vim</h3>

<p>My next stop was Vim. This is where things get interesting. VimL is the
most horrid extension language for an editor that I have had the
misfortune of working with. The thing is, Vim is a <em>fantastic</em> editor. I
don&#8217;t know what the author was thinking when he designed this language,
but I can only assume it was done during a binge drinking contest.</p>

<p>The only salvation is that there are Vim interfaces for a number of
languages, including Scheme, Ruby, and Python. Unfortunately, you still
have to write some VimL to glue it all together, but very little. The
most important stuff can be written in one of those decent languages and
you can generally keep your sanity. Seriously, go look at gist.vim, a
plugin for Vim for the <a href="https://gist.github.com">Gist</a> API. It is pure
VimL and a little over one thousand lines. No thanks.</p>

<p>I used Vim for a day and then I wrote that damned plugin. I dove in
head first. The most information I could find for the Vim interface to
any given language was for Python, and I was equally oblivious to all of
these languages so I went with it. The actual Python part of things was
dead simple. I spent maybe 3 hours on it (having not written a line of
Python before). Then I spent another day and a half on the VimL glue
part of things and I had a very nice and usable plugin. It was great!</p>

<p>At this point, I stopped caring about editors and was more concerned
with how much fun I had with Python! I don&#8217;t very often just write shit
in random languages just because I can, so it was nice to do new things.
So I began having crazy thoughts to justify a rewrite in Ruby. Everybody
loves Ruby! Nobody likes Python<sup>1</sup>! Let&#8217;s do it in Ruby!</p>

<p><a href="https://github.com/Raynes/refheap.vim">So I did it in Ruby</a>. This time, I
wrote a <a href="https://github.com/Raynes/rubyheap">whole API library</a> first because I
knew I&#8217;d be using it for more than refheap.vim. That was 6 different
kinds of fun. As a bonus, I <a href="https://github.com/Raynes/refh">wrote a command-line tool for RefHeap as well</a>.
The actual Vim part of things was easy because the VimL glue was already written
from the Python implementation.</p>

<p>I had lots of fun doing this, and it took me several days to do it. I
used Vim for all of it, and that&#8217;s where things got interesting. I
really liked Vim. I used Vim for about a week and a half probably 6
months before but gave up on it for Clojure development for one simple
reason: it is not as smart as Emacs about indentation.</p>

<p>In Emacs, when you press tab, it indents exactly how much it is supposed
to be indented. You can press tab for a month and it&#8217;ll only indent to
what is <strong>correct</strong> for that language. Now, this varies for languages
like Haskell because there is more than one correct indentation, but
this kind of variation does not exist in Lisp and thus Clojure. Where
this really begins to show is in an example like this:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nf">defn</span><span class="w"> </span><span class="nv">foo</span><span class="w"> </span><span class="p">[]</span><span class="w"></span>
</span><span class='line'><span class="w">  </span><span class="p">(</span><span class="nf">let</span><span class="w"> </span><span class="p">[</span><span class="nv">x</span><span class="p">]</span><span class="w"></span>
</span><span class='line'><span class="w">    </span><span class="p">(</span><span class="nf">y</span><span class="w"> </span><span class="nv">x</span><span class="p">)</span><span class="w"></span>
</span><span class='line'><span class="w">    </span><span class="p">(</span><span class="nf">z</span><span class="w"> </span><span class="nv">x</span><span class="p">)))</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>What happens if we want to take the <code>let</code> out of this code?</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nf">defn</span><span class="w"> </span><span class="nv">foo</span><span class="w"> </span><span class="p">[]</span><span class="w"></span>
</span><span class='line'>
</span><span class='line'><span class="w">    </span><span class="p">(</span><span class="nf">y</span><span class="w"> </span><span class="nv">x</span><span class="p">)</span><span class="w"></span>
</span><span class='line'><span class="w">    </span><span class="p">(</span><span class="nf">z</span><span class="w"> </span><span class="nv">x</span><span class="p">))</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Uh oh. Now what? We need to somehow reindent this whole thing so that
that the two forms are lined up against the <code>defn</code> now that the <code>let</code> is
gone. In Emacs, I would have walked through this hitting tab which would
reindent each line in turn for me. But Vim isn&#8217;t smart about
indentation it knows what to do when I hit return and insert a newline,
but if I hit tab some more, it&#8217;ll indent some more. I didn&#8217;t think Vim
had an answer to this until Chris Granger, a Clojurian vimerman, told me
the secret to life, the universe, everything.</p>

<p>Vim has a concept of &#8216;text objects&#8217;. Basically, this is a way to
intelligently select an area of text based on what it is surrounded by.
This is <em>fantastic</em> for Clojure and Lisp in general because everything
is surrounded by parentheses. So, to reindent this code, we can do
<code>=i(</code>.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="p">(</span><span class="nf">defn</span><span class="w"> </span><span class="nv">foo</span><span class="w"> </span><span class="p">[]</span><span class="w"></span>
</span><span class='line'>
</span><span class='line'><span class="w"> </span><span class="p">(</span><span class="nf">y</span><span class="w"> </span><span class="nv">x</span><span class="p">)</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="p">(</span><span class="nf">z</span><span class="w"> </span><span class="nv">x</span><span class="p">))</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Bang! And for the line that is left, we only have to hop down a line and
hit <code>dd</code>, which deletes the line. This one single command changed my
entire opinion of Vim.</p>

<p>Oh yeah, and I was really attached to paredit in Emacs. <a href="https://github.com/emezeske/paredit.vim">Bang!</a>.
Yes, this works really really well. It has all of the most important
commands (for me, anyways) and it keeps things balanced. It isn&#8217;t quite
as polished as the Emacs version, but it does work.</p>

<p>So, somewhere along the way, I became a Vim user. I haven&#8217;t used Emacs
in at least two weeks, probably 3, and I am wildly productive in Vim. I never really
used editor commands in Emacs (because I never remembered them long
enough to use them for anything significant, which is probably because
they took longer to enter than just doing the stuff manually).</p>

<p>Will I continue to be a Vim user forever? I thought I&#8217;d always use
Emacs, but that turned out to not be the case. I do not want to go back
to Emacs and do not see myself doing so in the near or distant future. I
might find a reason to do so tomorrow and I might never find a reason to
do so. We&#8217;ll see.</p>

<p>I&#8217;ve got one thing to add to this: I have nothing against Emacs.
Seriously guys, you don&#8217;t have to hate one in order to prefer the other.
If I had a good reason to use Emacs now, I would. I just started using
Vim and realized I liked it and that I&#8217;m very productive in it. Emacs
did its job just fine and I have no animosity towards it.</p>

<h2>So, my development setup</h2>

<p>This was supposed to be a post about my development environment but it
turned into a really long post about how I got here. I think that is
important. I need people to understand <em>why</em> I use the things I do
because that is wildly more important than what I actually use. So,
without further ado, here is what I&#8217;m using for development today and
how I&#8217;ve got it set up.</p>

<h3>Software</h3>

<p>First of all, Vim. The first thing I did when I started using Vim was
install <a href="https://github.com/carlhuda/janus">janus</a>. It gave me a bunch
of useful keybindings out of the box and a whole lot of useful plugins
that I probably wouldn&#8217;t have found otherwise. The most useful ones I&#8217;ve
found so far are CtrlP, NERDCommenter, NERDTree, and Fugitive (oh em gee
this is awesome). The most useful thing I&#8217;ve found not included with
Janus is tabular.vim.</p>

<p>Also useful from Janus is that it bundles some plugins for highlighting
markdown, mustache, haml, sass, scss, better Javascript, and git
commits. Janus really is a good way to get started. You get basically
everything you need and you don&#8217;t have to go hunting for how to do it.</p>

<p>I know what you&#8217;re interested in is my Clojure setup, and I&#8217;m probably
going to disappoint you with it. I use VimClojure, and the first thing I
did was delete anything even remotely related to nailgun. Remember I
said I never really used SLIME in Emacs? I haven&#8217;t even bothered trying
to do anything similar in Vim. I believe the author is rewriting the
stuff for nREPL, and I&#8217;ll probably give it a go once that is done, but
I&#8217;ll probably never use it much. I actually like <code>lein repl</code> and I use
it. I do <em>tons</em> of interactive development, I just don&#8217;t do it in my
editor. Am I missing out? Probably. Do I care as long as I still get
things done and am productive? God no.</p>

<p>Besides paredit.vim, that&#8217;s it for my Clojure setup. Just VimClojure&#8217;s
syntax highlighting and indentation stuff and Leiningen. Clojure is
simple. Don&#8217;t need much more than that.</p>

<p>Oh, I also use OS X. The Vim I use is MacVim and my terminal emulator is
iTerm2, simply because it is less quirky than Terminal.app.</p>

<p>My color scheme is solarized dark unless I&#8217;m in the sun, in which case I
go with solarized light. I&#8217;m not entirely satisfied with solarized, but
there aren&#8217;t a lot of good color schemes with both light and dark
backgrounds. The color scheme I like the most and used before solarized
was Tomorrow Night. I really liked it, but there are some problems with
the Vim highlighting that I&#8217;m not entirely satisfied with, and Chris
Kempson, the author, doesn&#8217;t seem to care about it anymore
and never responds to pull requests (of which there are many) and
issues. I still use a tomorrow-nightish theme on RefHeap because I can
control it, but I don&#8217;t use it for my system anymore. If I had more
time, I&#8217;d just fork it and work on it myself, but it&#8217;s quite a task. I
sure wish somebody would though.</p>

<p>Also, iTunes. I consider music a part of my development environment.
Yes, I know about the disdain for iTunes, the reason I use it over other
media players is outside the scope of this blog post.</p>

<h3>Hardware</h3>

<p>I use a 2,2 macbook pro from &#8216;06 or &#8216;07. I did not pay a penny for this.
The company I work for, Geni, gave me this laptop. It is old, but it still
works really well and the hardware isn&#8217;t really ancient. It has 3GB RAM and
a 2.33GHz intel core 2 duo. The only issues I have with it is that it is rather
unsightly, has a DVI port, doesn&#8217;t have the newer and better multitouch trackpad,
and is big and bulky.</p>

<p>My ideal hardware would be a new 13-15 inch macbook pro, but I think I&#8217;d
be fine with a macbook air. In a pinch, I&#8217;d appreciate a middle class
thinkpad loaded with arch linux. Unfortunately, I am not a man of means
right now.</p>

<h2>Conclusion</h2>

<p>I am currently a Vim user recently converted from Emacs. If you&#8217;re new
to Clojure and you like Vim more than Emacs or are already familiar with
Vim, please ignore everybody who tells you that you <em>need</em> to use Emacs.
You will be <strong>just fine</strong> with Vim. I <em>chose</em> Vim after two years of
using Emacs, and I&#8217;ve yet to be dissatisfied. If you want to check out
Emacs, by all means, do so. Having it under your belt and having more
options is always good, but understand that it is absolutely not a
necessity for Clojure.</p>

<p>If you&#8217;ve stumbled upon this blog post trying to find the best way to
get started with Clojure, then you have found your answer. The answer is
&#8220;whatever you are comfortable with&#8221;. Go install
<a href="https://github.com/technomancy/leiningen">Leiningen</a> and use it instead
of trying to &#8216;install&#8217; Clojure, and then use whatever editor you are
familiar with. If you already like Emacs, use Emacs; if you like Vim,
use Vim; even if you&#8217;d be more comfortable starting with an IDE, you are
fine. Go get Counterclockwise for Eclipse or La Clojure for IDEA. If
it begins to not be enough for you, check out the editors. There is no
wrong answer here and no silver bullet. My friend Phil Hagelberg says it
best: whatever you do, do <em>not</em> let your editor get in the way of you
learning Clojure.</p>

<p>Thanks for reading.</p>

<p><sup>1</sup> This is a <strong>joke</strong>. Please take it as such. I don&#8217;t dislike
Python. Python is our friend. Take a deep breath.</p>

<p>Thanks a lot to <a href="http://brehaut.net/">Andrew Brehaut</a> for proof-reading
and advising me a bit. He made me do this, so if anything I say here
offends you, please direct all furious comments and bashing at his email
address. Thank you.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Waving Our Tentacles]]></title>
    <link href="http://blog.raynes.me/blog/2011/12/02/waving-our-tentacles/"/>
    <updated>2011-12-02T13:27:00-06:00</updated>
    <id>http://blog.raynes.me/blog/2011/12/02/waving-our-tentacles</id>
    <content type="html"><![CDATA[<p>Everybody knows that Github is fantastic. It is a wonderful service that not only hosts your Git repositories but also gives you a great platform for managing your projects and handling large-scale collaboration. It has arguably the best code review mechanism around (the pull request) as well as project wikis, issues, organizations, and even private repositories (on paid accounts) for the non-FOSS and business folk. It is an essential tool for the open source developer.</p>

<p>But we&#8217;re not really here to talk about Github as a service. We&#8217;re here to talk about Github&#8217;s new <a href="http://developer.github.com/v3/">v3</a> API and my Clojure library, <a href="https://github.com/Raynes/tentacles">tentacles</a>, that provides a Clojure interface to it.</p>

<p>While writing the library, I obviously had to test things as I went along. In doing this, I realized how elegant and powerful it is to work with APIs from Clojure if you have a simple and concise library for doing so. The Github API has some really neat things in it that are really fun to play with, even if we aren&#8217;t doing anything with them. This post is going to serve several purposes:</p>

<ol>
<li>We&#8217;re going to learn how to use Tentacles.</li>
<li>We&#8217;re going to learn how to use some important parts of the Github API.</li>
<li>We&#8217;re going to use the Clojure REPL as a prompt for interacting with the Github API &#8211; a very powerful concept.</li>
<li>We&#8217;re going to get to see what Tentacles can do.</li>
</ol>


<p>Note that I&#8217;ll be using two accounts in my examples: <code>Raynes</code>, my own account and <code>amalloy</code>, Alan Malloy&#8217;s account. It is about time he and I compete for world domination. Furthermore, I&#8217;m going to pretty-print some results with <code>clojure.pprint</code>.</p>

<h2>Playing along</h2>

<p>If you want to play along, go ahead and create a new project.</p>

<pre><code>lein new tentacles-fun
</code></pre>

<p>Next, edit your project.clj file and add <code>[tentacles "0.1.2"]</code> to your <code>:dependencies</code>. After that, just run <code>lein repl</code> and have a blast.</p>

<h2><a href="http://developer.github.com/v3/users/">Users</a></h2>

<p>Tentacles supports the entire Github API, but we&#8217;re going to start with Github&#8217;s user api. It has some very interesting data that we can look at.</p>

<p>We can, of course, get information about a user.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">require</span><span class="w"> </span><span class="o">&#39;</span><span class="p">[</span><span class="nv">clojure.pprint</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="nv">pp</span><span class="p">])</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">require</span><span class="w"> </span><span class="o">&#39;</span><span class="p">[</span><span class="nv">tentacles.users</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="nv">users</span><span class="p">])</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">pp/pprint</span><span class="w"> </span><span class="p">(</span><span class="nf">users/user</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="p">))</span><span class="w"></span>
</span><span class='line'><span class="p">{</span><span class="no">:followers</span><span class="w"> </span><span class="mi">30</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:following</span><span class="w"> </span><span class="mi">9</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:gravatar_id</span><span class="w"> </span><span class="s">&quot;1c6d7ce3810fd23f0823bf1df5103cd3&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:name</span><span class="w"> </span><span class="s">&quot;Alan Malloy&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:avatar_url</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;https://secure.gravatar.com/avatar/1c6d7ce3810fd23f0823bf1df5103cd3?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:bio</span><span class="w"> </span><span class="nv">nil,</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="no">:location</span><span class="w"> </span><span class="s">&quot;Los Angeles, CA&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:html_url</span><span class="w"> </span><span class="s">&quot;https://github.com/amalloy&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:public_repos</span><span class="w"> </span><span class="mi">56</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:created_at</span><span class="w"> </span><span class="s">&quot;2010-08-18T16:37:15Z&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:login</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:url</span><span class="w"> </span><span class="s">&quot;https://api.github.com/users/amalloy&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:email</span><span class="w"> </span><span class="nv">nil,</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="no">:type</span><span class="w"> </span><span class="s">&quot;User&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:public_gists</span><span class="w"> </span><span class="mi">160</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:hireable</span><span class="w"> </span><span class="nv">false,</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="no">:blog</span><span class="w"> </span><span class="s">&quot;http://hubpages.com/profile/amalloy&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:id</span><span class="w"> </span><span class="mi">368685</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:company</span><span class="w"> </span><span class="s">&quot;Geni&quot;</span><span class="p">}</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>You might note that Alan&#8217;s bio is nil. That&#8217;s because he has no life. Robot and all that.</p>

<p>Next, let&#8217;s learn a thing or two about ourselves. First, we&#8217;re going to need our authentication information quite a bit, so lets go ahead and def it so we don&#8217;t have to type our password 10 thousand times.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">def</span><span class="w"> </span><span class="nv">auth</span><span class="w"> </span><span class="s">&quot;Raynes:REDACTED&quot;</span><span class="p">)</span><span class="w"></span>
</span><span class='line'><span class="o">#</span><span class="ss">&#39;user/auth</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Truthfully, I just wanted to only have to do that once because I have a habit of forgetting to remove my password during demonstrations like this.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">pp/pprint</span><span class="w"> </span><span class="p">(</span><span class="nf">users/me</span><span class="w"> </span><span class="p">{</span><span class="no">:auth</span><span class="w"> </span><span class="nv">auth</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="p">{</span><span class="no">:disk_usage</span><span class="w"> </span><span class="mi">46932</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:collaborators</span><span class="w"> </span><span class="mi">0</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:plan</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="p">{</span><span class="no">:private_repos</span><span class="w"> </span><span class="mi">0</span><span class="w">, </span><span class="no">:collaborators</span><span class="w"> </span><span class="mi">0</span><span class="w">, </span><span class="no">:space</span><span class="w"> </span><span class="mi">307200</span><span class="w">, </span><span class="no">:name</span><span class="w"> </span><span class="s">&quot;free&quot;</span><span class="p">}</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:followers</span><span class="w"> </span><span class="mi">85</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:following</span><span class="w"> </span><span class="mi">36</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:gravatar_id</span><span class="w"> </span><span class="s">&quot;54222b6321f0504e0a312c24e97adfc1&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:name</span><span class="w"> </span><span class="s">&quot;Anthony Grimes&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:avatar_url</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;https://secure.gravatar.com/avatar/54222b6321f0504e0a312c24e97adfc1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:bio</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;My name is Anthony Grimes. I live in a small town called Eldridge in Alabama. I am a programmer and a very avid supporter of functional programming and Clojure. I am currently writing a book on Clojure called [Meet Clojure](http://meetclj.raynes.me). I&#39;m the author (or co-author) of a number of Clojure projects including [Try Clojure](http://tryclj.com) and [clojail](https://github.com/flatland/clojail), having spoken about the latter at Clojure Conj 2011. I am currently employed by [Geni](http://geni.com) as a paid intern working with Clojure on the backend.&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:location</span><span class="w"> </span><span class="s">&quot;USA, AL&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:html_url</span><span class="w"> </span><span class="s">&quot;https://github.com/Raynes&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:owned_private_repos</span><span class="w"> </span><span class="mi">0</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:public_repos</span><span class="w"> </span><span class="mi">58</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:created_at</span><span class="w"> </span><span class="s">&quot;2009-02-14T03:25:23Z&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:private_gists</span><span class="w"> </span><span class="mi">137</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:login</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:url</span><span class="w"> </span><span class="s">&quot;https://api.github.com/users/Raynes&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:email</span><span class="w"> </span><span class="s">&quot;i@raynes.me&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:type</span><span class="w"> </span><span class="s">&quot;User&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:public_gists</span><span class="w"> </span><span class="mi">457</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:total_private_repos</span><span class="w"> </span><span class="mi">0</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:hireable</span><span class="w"> </span><span class="nv">false,</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="no">:blog</span><span class="w"> </span><span class="s">&quot;http://blog.raynes.me&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:id</span><span class="w"> </span><span class="mi">54435</span><span class="w">,</span>
</span><span class='line'><span class="w"> </span><span class="no">:company</span><span class="w"> </span><span class="s">&quot;Geni&quot;</span><span class="p">}</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>As you can see, I definitely have a life.</p>

<p>So, what&#8217;s up with how we gave tentacles our authentication info? We passed it a map containing an <code>:auth</code> key. This is an idiom in tentacles. The Github API has a lot of API calls that can have <em>required</em> as well as <em>optional</em> arguments. Most of the POST api calls take a JSON hash of data (but not all of them). Tentacles represents this in a very simplistic way. Any API call that takes optional arguments or does something special when given auth info translates to a function whose last argument is an optional map. Any API call that requires authentication, regardless of whether or not it has optional arguments, must take an options map (even if it will only ever contain the auth info). Furthermore, any <strong>required</strong> arguments that an API call takes are required positional arguments to the equivalent tentacles function.</p>

<p>Okay, so I have this theory. I am pretty certain that I am much more popular than Alan. I&#8217;m nicer, arguably smarter, definitely better looking, and I certainly wear better clothes. Can the Github API tell me if I&#8217;m correct? You bet it can! The obvious and canonical measure of epeen size is follower count! Let&#8217;s tally &#8216;em up!</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">pp/pprint</span><span class="w"> </span><span class="p">(</span><span class="nf">users/followers</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="p">))</span><span class="w"></span>
</span><span class='line'><span class="p">[{</span><span class="no">:login</span><span class="w"> </span><span class="s">&quot;MayDaniel&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:avatar_url</span><span class="w"></span>
</span><span class='line'><span class="w">  </span><span class="s">&quot;https://secure.gravatar.com/avatar/e917432c8e85f043647b2c57b443c56f?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:url</span><span class="w"> </span><span class="s">&quot;https://api.github.com/users/MayDaniel&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:gravatar_id</span><span class="w"> </span><span class="s">&quot;e917432c8e85f043647b2c57b443c56f&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:id</span><span class="w"> </span><span class="mi">188450</span><span class="p">}</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="p">{</span><span class="no">:login</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:avatar_url</span><span class="w"></span>
</span><span class='line'><span class="w">  </span><span class="s">&quot;https://secure.gravatar.com/avatar/54222b6321f0504e0a312c24e97adfc1?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:url</span><span class="w"> </span><span class="s">&quot;https://api.github.com/users/Raynes&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:gravatar_id</span><span class="w"> </span><span class="s">&quot;54222b6321f0504e0a312c24e97adfc1&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:id</span><span class="w"> </span><span class="mi">54435</span><span class="p">}</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="p">{</span><span class="no">:login</span><span class="w"> </span><span class="s">&quot;tonyl&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:avatar_url</span><span class="w"></span>
</span><span class='line'><span class="w">  </span><span class="s">&quot;https://secure.gravatar.com/avatar/43b2ca65e7a2cadf849adf103e6c066d?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:url</span><span class="w"> </span><span class="s">&quot;https://api.github.com/users/tonyl&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:gravatar_id</span><span class="w"> </span><span class="s">&quot;43b2ca65e7a2cadf849adf103e6c066d&quot;</span><span class="w">,</span>
</span><span class='line'><span class="w">  </span><span class="no">:id</span><span class="w"> </span><span class="mi">31147</span><span class="p">}</span><span class="w"></span>
</span><span class='line'><span class="w">  </span><span class="nv">...</span><span class="p">]</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Okay, so that isn&#8217;t very helpful. Github gives us very verbose information about followers and it gives us <em>a lot</em> of info. Let&#8217;s trim that down a bit.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">pp/pprint</span><span class="w"> </span><span class="p">(</span><span class="nf">map</span><span class="w"> </span><span class="no">:login</span><span class="w"> </span><span class="p">(</span><span class="nf">users/followers</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="p">)))</span><span class="w"></span>
</span><span class='line'><span class="p">(</span><span class="s">&quot;MayDaniel&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;tonyl&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;jColeChanged&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;hoggarth&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;ossareh&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;stonegao&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;ohpauleez&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;raek&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;segfaulthunter&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;invaliduser&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;gigasquid&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;Clinteger&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;odekopoon&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;devn&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;dbyrne&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;imissmyjuno&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;k4rtk&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;xmlblog&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;jeremyheiler&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;amcnamara&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;sconover&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;srid&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;mbacarella&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;carlwarnick&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;spoon16&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;octopusgrabbus&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;daviddavis&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;andreisavu&quot;</span><span class="w"></span>
</span><span class='line'><span class="w"> </span><span class="s">&quot;RafalBabinicz&quot;</span><span class="p">)</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>That&#8217;s better. Now count them.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">users/followers</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="p">))</span><span class="w"></span>
</span><span class='line'><span class="mi">30</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Huh? That&#8217;s no good! He definitely has more followers than that. What&#8217;s going on here is that Github paginates results. By default, there are 30 items per page of results and the maximum number of results per page is 100. Let&#8217;s go ahead and set it to 100.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">users/followers</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">30</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Oh&#8230; He really does only have 30 followers. Well then. Moving on.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">users/followers</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">85</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>I think I&#8217;ve won this one. It&#8217;s the hair that does it, I think. There is also a specialized call to get the currently authenticated user&#8217;s followers, so we could have used it instead:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">users/my-followers</span><span class="w"> </span><span class="p">{</span><span class="no">:auth</span><span class="w"> </span><span class="nv">auth</span><span class="w"> </span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">85</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Any way you skin it, I&#8217;m better looking than Alan. But who is the better person? Who is the most&#8230; outgoing? Let&#8217;s find out!</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">users/following</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">9</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>9&#8230; What about me?</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">users/following</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">36</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>This just isn&#8217;t looking good for ol&#8217; Alan. That&#8217;s enough of that, before I hurt his feelings.</p>

<p>This isn&#8217;t all you can do with the user API. Check out the <a href="http://raynes.github.com/tentacles/#tentacles.users">docs</a> if you want to see the rest.</p>

<h2><a href="http://developer.github.com/v3/repos/">Repos</a></h2>

<p>So far, we&#8217;ve found out that I am better looking than Alan, more popular, and that I am a better person. But that isn&#8217;t all that matters! Who is the most hardcore coder? Tentacles tentacles on the octocat, who of us two does not code trash?</p>

<p>Our first measure will be a repository count. If I have more repositories than he does, then obviously I love coding more than him. Github has a dedicated API call for getting the authenticated user&#8217;s repositories, but we don&#8217;t want to use that, since it would also list private repos. While I don&#8217;t actually have any of those, I don&#8217;t want to be accused of skewing these numbers. This is srs bsns! Nonetheless, tentacles has a function for that called <code>repos</code>, just in case you need it (for cheating).</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">require</span><span class="w"> </span><span class="o">&#39;</span><span class="p">[</span><span class="nv">tentacles.repos</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="nv">repos</span><span class="p">])</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/user-repos</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">58</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Wow! I have 58 repositories! Let&#8217;s see how many of those are forks.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">filter</span><span class="w"> </span><span class="no">:fork</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/user-repos</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">})))</span><span class="w"></span>
</span><span class='line'><span class="mi">27</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Wow! 27. I&#8217;m a forkaholic. Now for Alan.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/user-repos</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">56</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Ooooooh, it&#8217;s close. It all comes down to the forks now.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">filter</span><span class="w"> </span><span class="no">:fork</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/user-repos</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">})))</span><span class="w"></span>
</span><span class='line'><span class="mi">38</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>HAH! Now, I&#8217;m not good at math, but I&#8217;m pretty sure that means I have more original repositories than he does. Cause that&#8217;s how I roll. Obviously, given these numbers, I love this craft waaaaaaay more than Alan does.</p>

<p>But who has the most popular code? I say we compare the number of watchers on our most popular projects.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/watchers</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>I highly doubt that that is an even 100. It probably just cut off at 100 because that&#8217;s our max. Let&#8217;s check the next page.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/watchers</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">2</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">27</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>127 watchers! What about my biggest project?</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/watchers</span><span class="w"> </span><span class="s">&quot;flatland&quot;</span><span class="w"> </span><span class="s">&quot;lazybot&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">53</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Oh noes! Maybe that isn&#8217;t my biggest one.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/watchers</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="s">&quot;tryclojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">57</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>._. I concede. He wins this one. Alan obviously has the most popular projects. You can&#8217;t win &#8216;em all.</p>

<p>Just for fun, let&#8217;s look at language stats:</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">pp/pprint</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/languages</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="s">&quot;tryclojure&quot;</span><span class="p">))</span><span class="w"></span>
</span><span class='line'><span class="p">{</span><span class="no">:Clojure</span><span class="w"> </span><span class="mi">7419</span><span class="w">, </span><span class="no">:JavaScript</span><span class="w"> </span><span class="mi">4471</span><span class="p">}</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">pp/pprint</span><span class="w"> </span><span class="p">(</span><span class="nf">repos/languages</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="p">))</span><span class="w"></span>
</span><span class='line'><span class="p">{</span><span class="no">:Shell</span><span class="w"> </span><span class="mi">527</span><span class="w">, </span><span class="no">:Clojure</span><span class="w"> </span><span class="mi">159307</span><span class="w">, </span><span class="no">:JavaScript</span><span class="w"> </span><span class="mi">106110</span><span class="p">}</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>We&#8217;ve both employed some tricks to make it so Github doesn&#8217;t think our projects are Javascript projects. I don&#8217;t store my JS dependencies in the repo and Alan puts them in a directory that Github doesn&#8217;t care about. We&#8217;re both clever, in any case.</p>

<p>This doesn&#8217;t even touch what the repo API is capable of. With Github&#8217;s v3 API, even download uploads are supported! Check out the full API in the marginalia <a href="http://raynes.github.com/tentacles/#tentacles.repos">docs</a></p>

<h2><a href="http://developer.github.com/v3/issues/">Issues</a></h2>

<p>I wonder who has the worst code? I bet that if we check the number of issues on our top projects, we&#8217;ll be able to find this out.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">issues/issues</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="s">&quot;tryclojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">0</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Bang! No open issues. &#8216;Cause that&#8217;s how I roll. Let&#8217;s check the closed ones as well.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">issues/issues</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="s">&quot;tryclojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:state</span><span class="w"> </span><span class="s">&quot;closed&quot;</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">20</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>So a total of 20 issues. What about 4Clojure?</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">issues/issues</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">29</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Ahahaha! They&#8217;ve got more issues <em>open</em> than I have open and closed combined.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">issues/issues</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:state</span><span class="w"> </span><span class="s">&quot;closed&quot;</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>Doesn&#8217;t even fit on one page!</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">issues/issues</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="s">&quot;4clojure&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:state</span><span class="w"> </span><span class="s">&quot;closed&quot;</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">2</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">59</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>159 closed issues! Wow! That must be some terrible code. I feel sad for the little fellow.</p>

<p>As with the rest of our examples, this is only the tip of the iceberg. Check out the Marginalia <a href="http://raynes.github.com/tentacles/#tentacles.issues">docs</a> for more.</p>

<h2><a href="http://developer.github.com/v3/gists/">Gists</a></h2>

<p>Finally. One final competition. Gists. The way I see it, if I have more gists than Alan does, then I&#8217;m&#8230; uh&#8230; I&#8217;m just a damned better person. I don&#8217;t need a reason.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">require</span><span class="w"> </span><span class="o">&#39;</span><span class="p">[</span><span class="nv">tentacles.gists</span><span class="w"> </span><span class="no">:as</span><span class="w"> </span><span class="nv">gists</span><span class="p">])</span><span class="w"></span>
</span><span class='line'><span class="nv">nil</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;amalloy&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">2</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">60</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>160 gists. Not bad. I bet I can do better.</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='clojure'><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">2</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">3</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">4</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">100</span><span class="w"></span>
</span><span class='line'><span class="nv">user&gt;</span><span class="w"> </span><span class="p">(</span><span class="nf">count</span><span class="w"> </span><span class="p">(</span><span class="nf">gists/user-gists</span><span class="w"> </span><span class="s">&quot;Raynes&quot;</span><span class="w"> </span><span class="p">{</span><span class="no">:per-page</span><span class="w"> </span><span class="mi">100</span><span class="w"> </span><span class="no">:page</span><span class="w"> </span><span class="mi">5</span><span class="p">}))</span><span class="w"></span>
</span><span class='line'><span class="mi">57</span><span class="w"></span>
</span></code></pre></td></tr></table></div></figure>


<p>457 public gists! PUBLIC ONES! I am obviously, for reasons I am not certain of, the better person.</p>

<p>The Gist API is extensive and complete. It even includes a way to create gists now! Check out the rest in the Marginalia <a href="http://raynes.github.com/tentacles/#tentacles.gists">docs</a>.</p>

<h2>Conclusion</h2>

<p>I&#8217;m better than Alan.</p>

<h2>Real Conclusion</h2>

<p>The Github API is awesome. Clojure is an awesome language for working with APIs. Working with APIs interactively is fun. Tentacles is fun. I like Tentacles. Tentacles is cool.</p>

<p>Thus concludes our demonstration. This post only just touches the capabilities of the Github v3 API. Tentacles supports all of it. Check out the Marginalia <a href="http://raynes.github.com/tentacles/">docs</a> and the Github API <a href="http://developer.github.com/v3/">docs</a> for more awesome.</p>

<p>Finally, these numbers/stats are obviously meaningless, so don&#8217;t look into them too much. It&#8217;s all in good fun.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[The Clojure Community and Me]]></title>
    <link href="http://blog.raynes.me/blog/2011/11/27/the-clojure-community-and-me/"/>
    <updated>2011-11-27T08:25:00-06:00</updated>
    <id>http://blog.raynes.me/blog/2011/11/27/the-clojure-community-and-me</id>
    <content type="html"><![CDATA[<h2>Origins</h2>

<p>When I was around 13 years old, I played a game called Diablo II. You&#8217;ve probably heard of it or played it. It was a fun little RPG that had a huge community of hackers surrounding it. I would often visit the blizzhacker&#8217;s IRC channel and chat with the community of programmers, or just listen in awe as they talked about amazing things that made no sense to me and left me baffled at how anyone could understand it. One person in particular was a Haskell programmer. He was a very smart fellow that I looked up to and whose opinion I valued. Some of these people were C#, C, and even D programmers. This was my first introduction to programming.</p>

<p>I decided that this was something I was interested in and would like to do, so I spent the next couple of months beating my head against Python, C#, Boo, and a number of other languages that I probably played with and simply don&#8217;t remember. Nothing really made much sense during that time. Python was the closest I got to things clicking. I had a lot of time to do these things because I&#8217;ve always been home-schooled.</p>

<p>I always say that Haskell was my first language. That is not entirely true. I played with many languages in the beginning. Haskell was, however, my first <em>real</em> introduction to programming. It was the first language that made sense. It was through Haskell that I was able to make sense of programming as a whole, including Object Oriented programming.</p>

<p>A lot of people wonder if learning Haskell, a purely functional language, as a first language would make OOP languages be as difficult to learn and understand as functional languages are to OOP developers. My answer, given my experiences, is no. Learning a functional language first gave me the programming foundations that I needed to understand OOP and how it compared to other paradigms. Basically, Haskell told me &#8220;Hey, this is programming. This is what it means and this is what you do.&#8221; and then Object Orientation was just another layer on top of that. Another way of doing things. Programming is simple, but OOP clouds that and makes it look unnecessarily complex. If I had to teach someone to program, I think I&#8217;d start with a functional language given my experiences.</p>

<h2>Enter Clojure</h2>

<p>So what comes after Haskell? After I had used Haskell for a couple of months and had got a few projects under my belt, it was time for a new step. I felt that my next logical step was a Lisp. Clojure was new and getting a lot of attention. It was very practical, had an amazing set of minds behind it, and a very welcoming tight-knit community.</p>

<p>I didn&#8217;t do a whole lot with Clojure at first. I spent a lot of time just learning the language and not actually doing anything. My first serious Clojure project was <a href="https://github.com/flatland/lazybot">lazybot </a> (originally known as sexpbot), an extensible IRC bot. It was a great project that I still use and actively maintain.</p>

<p>Originally, lazybot was a Pircbot. While Pircbot is an excellent Java library and is easy enough to use from Clojure, I really wanted to have a Clojure IRC library to use for it. That was my next project, <a href="https://github.com/flatland/irclj">Irclj</a>. Irclj is an asynchronous (a word that I didn&#8217;t understand at the time of writing it) IRC protocol library that I am currently in the process of rewriting. Nonetheless, it was my first introduction to lower-level network programming and the first time I&#8217;d ever done anything with any internet protocol. You can find lazybot in its current incarnation in the #clojure IRC channel where he provides services such as Clojure and Haskell code evaluation.</p>

<p>That project was my gateway into the world of serious programming. I spent a lot of the next year working on lazybot and writing plugins for it. I had some other projects here and there, but lazybot was the one that mattered the most to me.</p>

<h2>Things People Enjoy</h2>

<p>If I&#8217;m known for anything at this point, then I&#8217;m known for <a href="http://tryclj.com">Try Clojure</a>. It is a website that, like <a href="http://tryruby.org">TryRuby</a>, is designed to introduce people to the Clojure language very quickly by providing an interactive tutorial and an in-browser REPL that evaluates code <strong>on the server side</strong>. This was accomplished originally using Heinz N. Gies&#8217;s clj-sandbox Clojure sandboxing library and eventually one that my best friend Alan Malloy and I  wrote later, called <a href="https://github.com/flatland/clojail">Clojail</a>.</p>

<h2>How To Get Famous</h2>

<p>Clojure Conj 2010 brought a lot of firsts for me. It was my first trip outside of the state of Alabama where I was born, raised, and still live; my first time flying in an airplane; my first conference; and the first time that I felt that I was a real member of a community. It was, in general, a huge leap in my life. The last two years have seen me grow the courage to consider myself a &#8216;real&#8217; programmer, and begin programming professionally.</p>

<p>But it wasn&#8217;t really just the conference that did these things for me. It was the months leading up to the conference.</p>

<p>I wasn&#8217;t going. I was 16 and I didn&#8217;t have a job. My family is not particularly poor, but with the bulk of our income coming from disability checks, conferences were just not really something that could ever possibly happen. But I was okay. I mean, I was sad that I&#8217;d miss such a huge event, but I never expected to be able to go in the first place so the realization that it wasn&#8217;t happening was easy to take.</p>

<p><img class="left" src="http://1.gravatar.com/avatar/1dd69a4fb995d41e4f5d3c9e786d2d64?size=420" title="Chas Emerick" >
I&#8217;m not sure exactly how it happened, but I think somebody in the #clojure IRC channel asked me if I was going to the conference. Either that or I mentioned it for some reason. <a href="http://cemerick.com/">Chas Emerick</a> happened to be watching. He immediately messaged me privately and asked me why I wasn&#8217;t going to be able to make it. I told him that I simply did not have the funds to travel to North Carolina, pay for lodging, and register at the conference. Furthermore, it wouldn&#8217;t be just me &#8211; my mother would need to travel with me (for obvious reasons). He said, in so many words, &#8220;well, let&#8217;s see what we can do&#8221;.</p>

<p>Not much time passed before Chas told me <a href="http://thinkrelevance.com/">Relevance</a> had happily agreed to cover my stay at the hotel and waive the conference registration fee. I was floored. Chas decided that the best way to get the rest of the funds for air travel and miscellaneous travel expenses would be to hold a fund-raiser. He <a href="http://cemerick.com/2010/09/10/a-clojure-scholarship-lets-send-raynes-to-the-conj/">wrote a blog post</a>, set the goal for $1000, and threw up a paypal donate button. And so it began.</p>

<p>Oh how the twitterverse rooted for Anthony Grimes on that day. Hacker News, Reddit, IRC. Even people outside of the Clojure community itself were donating for my cause. A whopping 72 minutes later, our goal had been reached (and slightly exceeded). 72 minutes is what it took for the Clojure community and others to raise $1000 for a 16 year old kid to be able to attend his first conference. Chas bought my mother and I our plane tickets and I was on my way.</p>

<p>This would spark what is now becoming a tradition. For this last conference, Chas Emerick once again took it upon himself to <a href="http://cemerick.com/2011/09/20/2011-clojure-scholarship-help-send-ambrose-to-the-conj/">help</a> somebody get to the conference. Ambrose Bonnaire-Sergeant had to travel much farther than me and thus his expenses were much greater. The Clojure community once again rose to the task and met the goal.</p>

<h2>Clojure Conj 2010</h2>

<p>Oh wow. What a whirlwind. I honestly only remember the very best parts of the whole trip. It&#8217;s all just a big bright and colorful blur.</p>

<p>I arrived at the hotel and walked into the lobby. How appropriate was it that the first person I met and talked to was Chas Emerick, sitting right there in the lobby with his laptop as if he were waiting on me. I resisted the urge to hug and kiss him and instead opted to shake his hand.</p>

<p>When I checked in to get my room key, the woman at the counter said, and I quote, &#8220;It&#8217;s about time! Everybody has been asking if you had arrived.&#8221;. That certainly blew my mind.</p>

<p>The next two people I met, also appropriately, were Lance Bradley and Justin Balthrop, two of my very best friends and people who would eventually be my co-workers. I didn&#8217;t even get to sit down once I got to the room, they already had me walking with them to a restaurant. We had fun that night.</p>

<p>The next day was even crazier than the last. Everyone who saw me wanted to shake my hand and talk to me. People would ask me about my experiences and praise me for my projects. I felt so much like a celebrity. Even Rich Hickey himself would speak to me and shake my hand later on.</p>

<p>The conference was wonderful. The talks were great, but the social aspect of it is what truly made it the most important experience of my life. Meeting everybody for the first time. The energy that emanated from everyone pulsed so hard that it could almost make your teeth rattle.</p>

<h2>2011</h2>

<p>The next year was a blast for me. A few months  after the conference, I was hired as an intern at <a href="http://geni.com">Geni</a>, working remotely under Justin Balthrop and Lance Bradley, two close friends, on the site&#8217;s backend with Clojure. With Clojure! My first job is developing Clojure! Winning! I quickly learned that making money is wonderful and that Macbook Pros are the best things since sliced bread.</p>

<p>At about the same time I got my job, I also landed a book deal with <a href="http://nostarch.com">No Starch Press</a>. I&#8217;m currently writing a book called <a href="http://meetclj.raynes.me">Meet Clojure</a> for them.</p>

<h2>Clojure Conj 2011</h2>

<p>The moment Clojure Conj 2011 was announced, I was plotting my way there. Clojure conferences are wildly addictive. Worse than heroin. But hey, I was making some money now. But hey, this conference is going to cost even more than the last. But hey, maybe Geni will pay for some of it? But hey, I&#8217;m 17, so liability is a big complicated legal bitch. Mostly, I knew I would get there <em>some how</em>, but I hadn&#8217;t quite worked out exactly how. If I had to, I would just save my paychecks for a month or so and not spend very much at all. This would have worked out, since I still live at home and have the privilege of being fed by a lovely woman with excellent shopping and frying pan foo. But hey, it didn&#8217;t have to work out.</p>

<h3>Speaking</h3>

<p>I vowed that I would not submit a talk for Clojure Conj 2011. I was utterly certain that I would not be able to perform a 40 minute long presentation in front of the entire Clojure community and be able to keep them interested while doing it.</p>

<p>But damn them all to hell, Justin, Lance, <strong>and</strong> Alan decided that they would submit talks. Every single Clojure person at Geni was submitting a talk except for me. I conceded, assuming that there would be no earthly chance of my talk getting chosen, and submitted a talk about JVM sandboxing and Clojail.</p>

<p>So, my talk got accepted. As soon as I cleaned out my undergarments and finished receiving congratulations from my friends, I went and cleaned them out once more. This <em>was</em> great though. It meant that Relevance would pay for my room (which also meant my mother&#8217;s room), waive the conference registration fee, and even give me a travel stipend (once I arrived) that would cover my own plane ticket. That dropped the total costs of the conference (disregarding miscellaneous expenses) to around $250. Winning!</p>

<h3>Actually Doing It</h3>

<p>But shit, now I have to compose and perform a talk. For 40 minutes. In front of Rich Hickey and everyone else that I am intimidated by and who is exceedingly smarter than I am.</p>

<p>So, I did what any person this terrified would do. I forgot it happened and waited until about two months before the conference before I even thought about writing the talk. I ended up doing the majority of it over about two weeks because Justin had given me the ultimatum that I would either practice it live with my co-workers over Skype or not get paid. ;)</p>

<p>It went surprisingly well over Skype. They gave me insanely awesome feedback and  a lot of encouragement. The Clojail talk was the product of not only my blood sweat and tears, but those guys as well. That they convinced and (half-serious) forced me to practice the talk was the best thing that could happen to me. It showed me how nervous I would be at the Conj and taught me how to control it.</p>

<h3>The Conference</h3>

<p>I was once again greeted with a healthy round of handshakes after my arrival at the venue. Early registration was great. I met Carin Meier, a person whom I had worked with on <a href="http://4clojure.org">4Clojure</a>. A handshake would not do for her, no, she wanted a hug. That was when I really felt that things would be okay and that, regardless of what happened with my talk, everything would work out in the end.</p>

<p>And it did. After meeting my best friend in person for the first time, Alan Malloy; after Carin Meier&#8217;s big ol&#8217; bear hug; after letting the atmosphere of Clojure Conj 2011 and the Clojure community once again sink in and take hold of me, I felt so good that I didn&#8217;t bother practicing my talk again before I performed it. I was okay.</p>

<p>My talk went great. It was well received and people praised me for it for the remainder of the conference. Either immediately after or during my talk, Rich Hickey <a href="https://twitter.com/#!/richhickey/status/134672231140835328">tweeted</a> (a rare occurrence) the following: &#8220;@IORayne is awesome, killer job on library and talk! #clojure_conj&#8221;. And if that wasn&#8217;t enough to make me piss myself in glee, he approached me right after my talk, shook my hand, and said the equivalent in person. This man, the public speaker of the decade, praised me on my speaking abilities. Holy. Shit.</p>

<p>After that, I did a brief interview with Chas for his podcast <a href="http://mostlylazy.com/">Mostly Lazy</a>, and a slightly longer interview with <a href="http://codebassradio.net/shows/runtime-expectations/">Runtime Expectations</a> of Codebass radio. Both should be made public soon.</p>

<p>Oh man, Clojure Conj 2011. I&#8217;ve never attended a non-Clojure Conj conference before, but surely this is as good as it gets, right? I can not think of a single thing that I did not like about the whole experience. Relevance knows how to put on a conference.</p>

<h2>Conclusion</h2>

<p>I&#8217;m not really sure why I wrote this. I suppose I did it for two reasons:</p>

<ol>
<li>At least 10 thousand people asked me about my origins at the Conj. Wondering how I got into programming and such.</li>
<li>I can not say enough good things about the Clojure community. Sometimes I just need to tell people about it, and I can&#8217;t think of a better way to do that than by pointing out that <strong>my entire life</strong> has been built around it.</li>
</ol>


<p>So, that&#8217;s me. Where I came from, what I&#8217;ve done, and more importantly, what the Clojure community has done for me. I owe so much to all of you that I can&#8217;t really express my thanks in words. Instead, I hope that my contributions to the world will be enough. My motivation is to make you proud.</p>

<h3>Future</h3>

<p>It&#8217;s nice to have one of these.</p>

<p>I don&#8217;t really know what the future will hold. At the very least, more projects will always be forthcoming. I want to make nice things. Furthermore, I do currently have a job, albeit an internship (a paid one), with Geni. I&#8217;d like to work with them full time at some point after I turn 18 and graduate high school. This would obviously involve extended stays in California and most likely a move, even if not immediately. That&#8217;s all some pretty serious stuff that I nor my co-workers are taking lightly. They are very supportive of me to the point that they allow me to work remotely, even though I am the only one who does so on a regular basis. This means that they have to actively put effort forth to keep me in the loop about things, since I&#8217;m not there to hear their conversations. I&#8217;ve never once saw them complain about it.</p>

<p>So, I think my future is bright. I&#8217;m happy that I&#8217;m busy and I have a lot of great things ahead of me. I look forward to the future and whatever it may bring.</p>

<p>Thanks for reading.</p>

<p>-Anthony</p>

<p><em>P.S. Many thanks to Dominik Picheta and Ricky Elrod for reading this for me and making sure I&#8217;m not insane.</em></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Moving Try Clojure to Heroku]]></title>
    <link href="http://blog.raynes.me/blog/2011/11/03/moving-try-clojure-to-heroku/"/>
    <updated>2011-11-03T00:46:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/11/03/moving-try-clojure-to-heroku</id>
    <content type="html"><![CDATA[<p>tl;dr: At first I was like &#8220;uhh&#8230;&#8221; but then I was like &#8220;AH!&#8221;.</p>

<p>I started playing with Heroku a bit recently. I had heard all sorts of wonderful things about it and figured it was worth giving a go. I never expected to get anything done because I wasn&#8217;t willing to put a lot of effort into it, since I was planning on just playing around with things.</p>

<p>So, my plan was to see if I could get <a href="http://tryclj.com">Try Clojure</a> running on Heroku. I took a seat and began reading documentation. Well, if a two minute blog post is documentation. The funny thing is, it wasn&#8217;t until I actually decided to move it to Heroku permenantly that I actually had to read official Heroku documentation, and that was only to find out how to get my domain pointed at the Heroku instance and to find out more info about how much the site would be able to handle on the free plan.</p>

<p>It took me all of 10 minutes to get Try Clojure running on Heroku. The only reason it took that long was because I didn&#8217;t know that I had to start my server on a port that Heroku sets the environment variable PORT to. So, about 8 of those 10 minutes were me committing a fix for that. It was less a less than 5 line change.</p>

<p>I had to modify its <code>project.clj</code> file to add a :jvm-opts line to set the Java policy file for the application, but that was only because cake reads jvm options from <code>.cake/config</code> and Leiningen takes a key in project.clj for them. At this point in time, Heroku only supports Leiningen on the Clojure stack. I&#8217;ve been told that cake is not out of the question in the future. It doesn&#8217;t really matter because it was a good idea to add :jvm-opts to my project.clj so that I could maintain complete Leiningen compatibility in Try Clojure. I have absolutely nothing against Heroku for not supporting cake &ndash; it simply isn&#8217;t a big deal, since Leiningen and cake have mostly compatible project descriptions and Try Clojure&#8217;s project file is very simple.</p>

<p>Finally, I had to create a <code>Procfile</code> containing the following:</p>

<pre><code>web: lein run
</code></pre>

<p>and this tells Heroku how to run my code.</p>

<p>After my code was set up, all I had to do was <code>sudo gem install heroku</code> and then <code>heroku create --stack cedar</code> and bang, my project was a Heroku project. To deploy, I simply ran <code>git push heroku master</code>, which pushed my code to the <code>heroku</code> remote that heroku set up when I ran the <code>create</code> task. The code was pushed, it ran my code with <code>lein run</code>, shot me a link to the app, and tada &ndash; Try Clojure runs on Heroku.</p>

<p>I was completely floored by how easy all of this was. And that&#8217;s how easy it <strong>always</strong> is. When I want to deploy, I just push my code to the heroku remote with git and Heroku handles the restarting and everything with absolutely no work on my part <em>at all</em>. This workflow won me over immediately.</p>

<p>So great, I have Try Clojure running on Heroku. Just an experiment and it worked really well. At this point, I had no intention of moving to Heroku permenantly. I didn&#8217;t know much about Heroku and how much the free account would give me.</p>

<p>I mentioned that it was running on Heroku on Twitter (and probably in the #clojure IRC channel as well, but I don&#8217;t quite remember). The next day, <a href="http://technomancy.us/">technomancy</a> (Phil Hagelburg) asked me in the IRC channel if the main Try Clojure instance was running on Heroku, and I told him the equivalent of &#8220;Hell no, I don&#8217;t know anything about Heroku and I only have a free account. I don&#8217;t trust Heroku enough to move it over.&#8221;. It struck me as odd when he replied with &#8220;Heh&#8221; followed by a cute joke, but it didn&#8217;t dawn on me why he did it.</p>

<p>The next day, I remembered that technomancy had just recently started working at Heroku himself! One of our very own popular Clojurians! I ended up talking with him (he kindly approached me, offering to help) later that day. I read a lot of documentation around the same time and asked him a few questions.</p>

<p>What confused me the most was the concept of a &#8216;dyno&#8217;. This is a process of any type running on Heroku. Processes for stuff like Mongrel or Resque and, of course, your app and its own processes. In my case, I have one web process, which is what I defined in my Procfile.</p>

<p>Heroku takes a time-based approach to its services and billing. The idea is that you pay for how many hours each dyno that you use has run in a given month, and every free account has 750 free dyno hours. If I only run one dyno all month, I don&#8217;t have to pay anything at all. And even if an app that I use was to get slashdotted or something similar that would generate massive amounts of traffic, all I&#8217;d have to do is run a command or two to scale things up. I could add a dyno or two and only run them for as long as the traffic was too hard for a single dyno to handle the traffic, then I could scale it back down and I&#8217;d hardly have to pay anything at all unless I used those dynos for quite a while. You only pay for what you use, and you aren&#8217;t obligated to use anything more than what you need/want!</p>

<p>This all made me feel much better about the whole thing. Try Clojure is a fairly simple site that doesn&#8217;t get <em>tons</em> of traffic, so I decided I&#8217;d go ahead and move the main instance to Heroku. I even bought a new domain, <a href="http://tryclj.com">tryclj.com</a> to celebrate the move.</p>

<p>Try Clojure is now running entirely on Heroku, and I&#8217;m really enjoying it. I haven&#8217;t had to scale things up at all at this point, even though it got pounded with traffic when Heroku tweeted about the move. I kept an eye on it during that time and it never even slowed down.</p>

<p>Really, what won me over is the workflow. It is <em>soooo</em> easy. I mean, deployments by pushing to a git remote? That&#8217;s just beautiful. I can make changes and deploy them faster than a Github employee! Finally, the fact that Phil was so eager to help me really says something about Heroku in my book. If they hire people like him, I bet the rest of their team is equally as lovely.</p>

<p>I look forward to a long and fruitful future with Heroku, and I thank them for their fantastic service and giving me a reason to not have to host and maintain that site on my own VPS.</p>

<p><a href="http://tryclj.com">Try Clojure&#8217;s</a> source code is on <a href="http://github.com/Raynes/tryclojure">Github</a>. Check it out if you&#8217;re interested in seeing a simple website that runs on Clojure, the excellent <a href="http://webnoir.org">Noir</a> web framework, and Heroku.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Problems With Clojure + Homebrew?]]></title>
    <link href="http://blog.raynes.me/blog/2011/10/22/problems-with-clojure-plus-homebrew/"/>
    <updated>2011-10-22T16:53:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/10/22/problems-with-clojure-plus-homebrew</id>
    <content type="html"><![CDATA[<p>There is a fix-all solution to any problems you&#8217;re having with Clojure + Homebrew.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ brew uninstall clojure</span></code></pre></td></tr></table></div></figure>


<p>Followed by</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ brew install cake</span></code></pre></td></tr></table></div></figure>


<p>Or</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ brew install leiningen</span></code></pre></td></tr></table></div></figure>


<p>In all seriousness, if you&#8217;ve installed Clojure via <em>any</em> package manager and you&#8217;re having problems, that&#8217;s because it doesn&#8217;t make any sense whatsoever to install Clojure through a package manager. The JVM ecosystem just doesn&#8217;t work that way. Clojure is essentially a library &#8211; it&#8217;s stored in a jar.</p>

<p>Instead, you want to install a build tool. <a href="http://github.com/flatland/cake">Cake</a> or <a href="http://github.com/technomancy/leiningen">Leiningen</a> are Clojure build tools. They are your &#8216;interface&#8217; to Clojure. They handle all the project management, classpath management, REPL management, compilation, and everything else that you shouldn&#8217;t need to or try to do on your own.</p>

<p>Do <strong>not</strong> try to &#8216;install&#8217; Clojure. Install a build tool instead. It&#8217;ll prevent 98% of all beginner issues related to classpaths and make it easier for people in the Clojure mailing list and IRC channel to help you with real problems. Furthermore, they make it really easy to manage everything from small scripts to large projects, and can even give you properly classpathed repls as easy as <code>cake repl</code> and <code>lein repl</code>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Meet Clojure Update]]></title>
    <link href="http://blog.raynes.me/blog/2011/10/09/meet-clojure-update/"/>
    <updated>2011-10-09T23:54:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/10/09/meet-clojure-update</id>
    <content type="html"><![CDATA[<p>This just in: progress, son.</p>

<p>Today was a great day for this budding author. I finished my next chapter, chapter 7 (tentative), and sent it to my editor. I&#8217;m not having any trouble at all keeping up with my deadlines.</p>

<p>Even better is that, about 20 minutes ago, I decided who I wanted my technical reviewer to be: <a href="http://chris-granger.com/">Chris Granger</a>, an insanely smart fellow and active member of the Clojure community. He is very interested in doing it, but nothing is official yet (I swear to God, all happened 20 minutes ago). I&#8217;ll be sure to tweet about it when it is official.</p>

<p>LibreOffice is much easier to face when you don&#8217;t have to do shit with it other than actually write the book and send each chapter individually. Of course, I&#8217;d still rather write my book in a plain text format, but beggars can&#8217;t be choosers and it works.</p>

<p>I&#8217;ve mostly been renovating mostly-complete chapters so far before sending them in. The real test will be writing new chapters from scratch, something that I&#8217;m going to have to face soon. I&#8217;m pretty sure I&#8217;ll make it though. I&#8217;m highly motivated because I have a fantastic technical reviewer, more communication with my publisher, and amazing friends encouraging me to get this sucker done.</p>

<p>Everything I&#8217;ve said before about the book still applies. There will be a free copy released when the book is finished that I am hoping will become an important entry-point for new Clojurians. I hope to do something similar to <a href="http://learnyouahaskell.com">Learn You A Haskell</a> and have a pretty HTML version of the book.</p>

<p>In summary, 2 chapters have been submitted to my publisher for review. Still targeting every 3 weeks for new chapters. Technical reviewer acquired but not entirely official yet. Morale is high.</p>

<p>Stay tuned.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Meet Clojure Update]]></title>
    <link href="http://blog.raynes.me/blog/2011/09/20/meet-clojure-update/"/>
    <updated>2011-09-20T14:35:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/09/20/meet-clojure-update</id>
    <content type="html"><![CDATA[<p>I haven&#8217;t written a status update for Meet Clojure in quite a while, so here it is.</p>

<p>Keith, my editor, emailed me a few weeks ago wanting to work out a new schedule for the book. Several months had passed with little communication between us and I didn&#8217;t get very much work done on the book, so this was a welcome surprise. We worked out a schedule: first chapter in 2 weeks and then a new chapter every 3. I finished the first chapter nearly a week before my September 22nd deadline.</p>

<p>This is good news. Chapter 4 is done and is currently undergoing review. It is one of the largest and most important chapters of the book, so it&#8217;s actually a pretty large chunk of progress.</p>

<p>We are very excited to have a real solid schedule for the book and I am anxious to get more of the book finished. Schedules are fantastic motivation. That said, our new schedule will certainly not allow for a 2011 release. There is isn&#8217;t even speculation as to when it&#8217;ll all be ready to ship at this point, so I can&#8217;t really provide any information on that. The important thing is that I am actively working on this and that I have real deadlines that it would not be good for me to miss.</p>

<p>As far as my relationship with Libre/OpenOffice goes, it&#8217;s still a little strained. I&#8217;ve found that most of my hatred for it came while I was converting the existing book from my Pandoc markdown workflow to the No Starch OpenOffice workflow. It was extreme tedium and required a lot of things that OpenOffice isn&#8217;t designed for.</p>

<p>It is much, much easier to actually write new content with OpenOffice. No Starch&#8217;s template switches styles intuatively and when appropriate all by itself, so I rarely have to do a whole lot of mouse stuff. I do miss Emacs and markup, and I definitely wish OpenOffice didn&#8217;t suck at keybindings on OS X, but I&#8217;ll survive.</p>

<p>In summary: Work being done. Progress being made. Unicorns being milked for their magic.</p>

<p>Stay tuned.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Moved to Octopress]]></title>
    <link href="http://blog.raynes.me/blog/2011/09/18/moved-to-octopress/"/>
    <updated>2011-09-18T23:17:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/09/18/moved-to-octopress</id>
    <content type="html"><![CDATA[<p>I spontaneously decided today that, while sick, I would spend the day setting up Octopress and trying it out. I did. I liked. I replaced wordpress with it. Unfortunately, this comes with a few consequences.</p>

<ul>
<li>I used <a href="https://github.com/thomasf/exitwp">exitwp</a> to import my Wordpress posts into Octopress. While it did its job importing the posts, it managed to clobber paragraph breaks and all of my code along the way. I&#8217;m going to try to fix this as best I can in my most important posts, but I&#8217;m not going to do it for all of them.</li>
<li>All of the old links are broken. I never used readable links with Wordpress and Octopress (rightfully) uses them by default. Luckily, my blog isn&#8217;t very popular so the link breakage will be less significant. I do apologize to anyone linking to my blog though and hope that this doesn&#8217;t inconvenience them too much.</li>
<li>All of the comments are gone. Comments didn&#8217;t move with the Wordpress post, and since the links have changed and I&#8217;m using Disqus now, there isn&#8217;t a simple way to retrieve them.</li>
</ul>


<p>I firmly believe that the benefits of switching to Octopress will outweigh the issues, even if the benefits only apply to me. I&#8217;m very excited to write my posts in Emacs and markdown and to be getting rid of a useless MySQL instance and several instances of php-cgi.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[All Media Centers Suck]]></title>
    <link href="http://blog.raynes.me/blog/2011/07/10/all-media-centers-suck/"/>
    <updated>2011-07-10T21:43:10-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/07/10/all-media-centers-suck</id>
    <content type="html"><![CDATA[<p>Recently, I&#8217;ve had a little down time thanks to appendicitis and a good
round of surgery, so I&#8217;ve been working on getting a media center set up
on my laptop. Unfortunately, none of them do everything that I require.
The big choices right now are XBMC, Plex, and Boxee. They&#8217;re all
excellent in some areas and pathetic in others.</p>

<h1>My Requirements</h1>

<p>I&#8217;m pretty picky about what I need from a media center. My needs are not
the same as everyone else. First of all, I have a poor internet
connection. It&#8217;s fast enough to stream from Netflix, but it&#8217;s a verizon
mifi card, and they don&#8217;t like it when you rape their bandwidth.
Therefore, I have to keep my streaming to a minimum. Because of that, I
have these things called DVDs. They&#8217;re these neat little discs that hold
movies. I get them from Netflix and Redbox and I put them into my little
disc drive and movies play on my screen. It works pretty well. I need
for my media center to handle, at a minimum, Pandora radio, Netflix
streaming, DVD playback, music playback, and the ability to control it
all from my android phone with a wifi remote. Sounds easy, doesn&#8217;t it?
Guess what. None of them do it.</p>

<h1>Boxee</h1>

<p>Boxee is great. It&#8217;s a little do-it-all media center that can run on
your computer or a dedicated &#8220;Boxee Box&#8221; that you can buy. Ever since
the Boxee box came out, development has been focused on it. Recently,
everybody started whining so they promised to pick development back up
and have a release out by&#8230; this fall. In any case, Boxee in its
current incarnation is very nice. It can play Pandora better than Plex,
and it can stream Netflix just fine. It can do a whole lot of other
stuff as well. Unfortunately, the latest version for the mac,
0.9.23.15885, cannot play DRM&#8217;d DVDS. They&#8217;ve disabled DVD support and
an issue in their issue tracking about it was closed as &#8216;won&#8217;t fix&#8217; with
no explanation. Goodbye Boxee. I hope that, in that next release, it&#8217;ll
be enabled. If the DVD support worked, Boxee would be absolutely
perfect.</p>

<h1>XBMC</h1>

<p>XBMC is the biggest one of them all. It&#8217;s the media center that the
other media centers we&#8217;re talking about were based on. It&#8217;s a fantastic
media center within itself. However, it doesn&#8217;t even come close to doing
what I need. Out of all the things I require, the only thing it does is
handle DVD playback and music playback. They haven&#8217;t even taken a shot
at adding Netflix and such. No clue why, seeing as how the media centers
like Boxee and Plex that are based on it do implement them. There are
third party add-ons for Netflix and Pandora though. Haven&#8217;t tried the
Pandora add-on, but the Netflix plugin, XBMC flicks, at this point in
time, doesn&#8217;t work. It hasn&#8217;t worked for the better part of this year.
Whether or not it&#8217;ll actually be fixed is up in the air. XBMC isn&#8217;t
really much of an option for me.</p>

<h1>Plex</h1>

<p>Plex is awesome. It is like Boxee in that it gets all of the streaming
and local media stuff right. Its Pandora support is a little wonky but
appears to work. The Netflix support is perfectly fine. The problem is
that it doesn&#8217;t support DVD playback in a meaningful way. It has
*some* support for it. You can turn it on and hit &#8216;dvd&#8217; in the menu.
It goes straight to the DVD menu. I couldn&#8217;t find any settings for
anything, and turning on subtitles is tedious and unintuitive. You can&#8217;t
even eject the disc when you&#8217;re done. Plex is great, but the DVD support
is basically worthless. The biggest thing that irks me is how little the
developers seem to care about DVD support. I&#8217;m beginning to wonder if
I&#8217;m the only person who sees the importance of it. For the past 2 years
or so, they&#8217;ve been saying things like &#8220;It&#8217;s on our radar.&#8221; and &#8220;We need
to improve that.&#8221; but haven&#8217;t done anything. Maybe in the next 10 years
or so when DVDs are obsolete they&#8217;ll finish up the DVD support. ;) Now,
there is a workaround. You can set up applications to launch from within
Plex. For example, you can make the mac&#8217;s default DVD player launch, and
when it&#8217;s closed, Plex will reopen. Nice! The problem is that, while I
*can* control Plex using the Plex remote from my Android phone, I
cannot control DVD player. Alas, the dream is dead. Until Plex has
decent DVD playback, I&#8217;m out of options. I&#8217;m extremely disappointed with
my findings. At this point in time, all media centers suck (in certain
ways).</p>

<h2>Updates: July 19, 2011</h2>

<p>Eventually I decided on Plex. I ripped my entire DVD collection so the
poor DVD support is less of an issue for me now. Furthermore, the
Pandora plugin crappiness is <a href="http://forums.plexapp.com/index.php/topic/4249-pandora-thank-you/page__view__findpost__p__87184">not their
fault</a>.
It turns out, Pandora is run by assholes who favor Boxee. I still think
Boxee is nice. Plex is nicer. The media server and manager is fantastic
and I can think of tons of uses for it in the future. Boxee plans to
release a new version this fall (after the majority of their userbase
had given up on them, assuming they had abandoned the downloadable
version for the Boxee Box version), so it remains to be seen if it can
win my heart. I doubt it. All it has that Plex doesn&#8217;t have that I want
is a decent Pandora app, and it isn&#8217;t even Plex&#8217;s fault that it doesn&#8217;t
have a good one. Boxee could be the best all around if it just had a few
more developers to spare towards the downloadable version. But, it&#8217;s
forgivable. They aren&#8217;t a huge company and they have to prioritize. I
just wish the Boxee Box wasn&#8217;t the priority. They had a dream and
they&#8217;re living that dream. Good for them!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[An Update on Meet Clojure]]></title>
    <link href="http://blog.raynes.me/blog/2011/06/12/an-update-on-meet-clojure/"/>
    <updated>2011-06-12T06:20:28-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/06/12/an-update-on-meet-clojure</id>
    <content type="html"><![CDATA[<p>Introduction Recently, I announced that my book, Meet Clojure, was
officially being published. This is true. Contract is signed, sealed,
and delivered. It even has its very own
<a href="http://meetclj.raynes.me">website</a>. The contract states that our
initial soft deadline is November. I have no clue when it will actually
be finished or when it will be in print, but I hope to have something
that I can bring to the Conj with me. Early release copies or something,
but it almost certainly won&#8217;t be in print by then. I&#8217;ll do something!
I&#8217;m very excited that people are excited about the book, and I haven&#8217;t
updated on my progress in a while, so that&#8217;s the purpose of this post.
First of all, there hasn&#8217;t been much progress up to this point. I
recently started a job writing Clojure at <a href="http://geni.com">Geni</a>, so I
took a little time off from the book to focus on that. This is my very
first job in the world, and it is very important to me that I am able to
give them my best and still be able to do this book. I know I can. I&#8217;ve
got a rhythm now, and I&#8217;m starting, slowly but surely, to continue work
on the book. I want my progress to be an open thing. Writing a book is
hard work, and a lot of people are confused about how the process works.
I know I was. I&#8217;m going to talk about what is going on, how I&#8217;m
approaching things, what exactly needs done, and what I&#8217;m working on
now. More importantly, I&#8217;m going to talk about the problems I&#8217;m facing.
Problems A very important part of writing a book is facing the problems
that come with it. As with programming, there are always issues and
&#8216;bugs&#8217; that you have to deal with. I&#8217;ve went through 3 different editors
and 3-5 different formats while writing this book. You know you&#8217;ve went
through hell when you can&#8217;t remember what all you&#8217;ve written it in. I
started out in Lyx, because it was the most approachable. Understand
that I never intended on this book getting published. I figured that it
would just be a totally free thing that I might self-publish dead tree
copies of. Therefore, I had to use the things that I could most easily
produce a nice looking PDF file from. Lyx was okay, but its editor is
really terrible to write in IMO, so I eventually migrated the entire
book to pandoc markdown. That was great. It was perfect. Unfortunately,
my publisher, No Starch, requires that I write the book in OpenOffice or
Word in order to take advantage of their stylesheet. I ended up spending
a sickening amount of time migrating the book to OpenOffice. Each time I
migrated the book to a new format, I had to do it by hand. The book
would probably be done by now if not for that. OpenOffice is where I&#8217;m
stuck, against my will. I&#8217;m dying for an asciidoc or markdown workflow
from No Starch at some point in time, but if it comes, it will almost
certainly be after my book is done with and finished. I could write
something to take a plaintext format such as markdown or asciidoc and
produce the ODT necessary, but with the stylesheet and such, I&#8217;d
probably spend more time with that than actually writing the book. So,
my biggest problem thus far has been facing OpenOffice (or LibreOffice,
which is actually what I&#8217;m using). I simply cannot stand writing in this
monstrosity. I spend more time clicking buttons and making sure my
styles are correct than writing my book. Even the keybindings are
awkward, since there is some bug in Open/LibreOffice that makes the ctrl
key useless on OS X. I feel I have enough experience right here and now
to declare that OpenOffice is simply not a good thing to have to write a
book in. Especially for a programmer who is used to Emacs. But bitching
about OpenOffice for three hours in a blog post isn&#8217;t going to get this
book written. I do not blame No Starch for this. They have their
workflow and it has worked fine for them before. They&#8217;ve just got a
whiny Emacs user on their hands. There really aren&#8217;t many more clear
&#8216;problems&#8217;. OpenOffice is one gigantic problem that I wish somebody
would fix. If somebody feels like writing a plaintext format -> ODT
converter that somehow magically supports OpenOffice stylesheets, I&#8217;ll
name my first born after you. I shit you not. Moving on. What needs to
be done, and what is being done Lots. Right now, I&#8217;m going through a
phase of &#8220;well, I wrote all of this 6 months ago. I want to do x and y
differently, so I need to change z and b and do n&#8221;. I find myself
frequently not satisfied with what I&#8217;ve done, and thus rewriting an
insanely large part of what is already done. This might sound like a
great thing for the eventual reader, and it is, but imagine what kind of
torment I have to face because of it. Right now, I&#8217;m reading the book
from cover to cover (at least, what is already written) and I&#8217;m
restructuring parts, rewriting parts, fixing parts, etc. There is a
*lot* of work left to do here, because a lot of the book is already
written. However, this is an important thing and would need to be done
regardless of whether it is now or later. It needs to be now because
then I can focus on how to structure the rest of the book. Next, there
are several chapters that aren&#8217;t finished or haven&#8217;t even been started
yet. Those need to be finished. Finally, I need to get up-to-date with
Clojure 1.3 so that I can be sure that this book is entirely Clojure 1.3
compatible. There is no sense in another book for an outdated Clojure.
Finally, on what needs to be done, I need to become accustomed to
OpenOffice so that I can get work done. I&#8217;m not sure it is possible to
be productive in this thing, but surely I can be useful. If I can keep
from hanging myself first. Now, about this book Some people seem to have
misunderstood me saying &#8220;This book is for complete beginners.&#8221; as
meaning that the book is for complete programming beginners. It
absolutely, positively, 100% is not. If you don&#8217;t have any programming
experience, you almost certainly won&#8217;t get through the first chapter.
This book is for complete beginners to <em>Clojure</em>, not programming in
general. I&#8217;m not going to explain what control flow means, or the
difference between an integer and a float. You need to have experience
in approximately one programming language, and the fundamentals of
programming should be clear to you. That said, this book is aimed at a
very wide audience. Anybody who programs should be able to use this
book. I&#8217;m working very hard to make this so, but it is a very difficult
goal to achieve and if it doesn&#8217;t pan out, I apologize in advance. One
thing you can feel safe knowing is that No Starch is very hardcore about
technical and developmental review. They&#8217;re going to kick my fat ass all
over Alabama until this book is worthy of reading, and I&#8217;m going to very
willingly take that punishment. Conclusion Writing a book is hard.
Writing a book in an environment you are unfamiliar with and hate is
even harder. But I&#8217;m being whiny. I can do this thing, I just need to
stop being lazy and focus. When I finish publishing this post and
tweeting about it and such, I am going to spend an hour or two on my
book. This whole thing is a lesson in discipline that I am grateful for.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Songbird in 2011]]></title>
    <link href="http://blog.raynes.me/blog/2011/06/05/songbird-in-2011/"/>
    <updated>2011-06-05T09:40:30-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/06/05/songbird-in-2011</id>
    <content type="html"><![CDATA[<p>So, I&#8217;m a secret long-time <a href="http://getsongbird.com/">Songbird</a> fan. I
know, I know. Once they dropped Linux support everybody (including
myself) ran like hell and never looked back. As it turns out, they may
have been right about their Linux user base &#8211; it wasn&#8217;t that
significant. Mostly, they just haven&#8217;t had quite as much publicity since
then, because people got understandably butthurt. However, Songbird has
continued to evolve, so one can only assume that they&#8217;re OS X and
Windows user base is still going strong. Of course, Songbird still works
on Linux and they put out &#8216;unofficial&#8217;
<a href="http://wiki.songbirdnest.com/Developer/Articles/Builds/Nightly_Builds">builds</a>
all the time, though they&#8217;re always labeled as &#8216;unofficial&#8217; and
&#8216;untested&#8217; (whatever that is supposed to mean). I actually tried it on
Linux and, save for some issues with conflicting gstreamers (Songbird
ships it&#8217;s own version of gstreamer), it worked fine. I don&#8217;t quite
understand what they mean by &#8220;we no longer support Linux&#8221; when, three
years later, they&#8217;re still putting out Linux builds and they still work.
I find it bizarre that they have volunteers that can handle this, but
they can&#8217;t handle at least some resemblence of official &#8216;support&#8217; for
Linux? Songbird is open source, so I highly doubt it&#8217;ll ever be broken
as long as Linux users that happen to be programmers are using it.
Nonetheless, I stopped using it when I moved to Linux a few years ago.
I&#8217;ve used everything else, but not Songbird. But now I&#8217;m on a mac, and I
don&#8217;t even want to think about touching iTunes. My good ol&#8217; Clementine
is fantastic, but Songbird supports all sorts of stuff that it doesn&#8217;t,
with CD ripping being a good example. Of course, Clementine will get all
the features that I like at some point because it has an awesome
developer, and the things it doesn&#8217;t support are things I don&#8217;t really
need, but hey, I&#8217;m on a platform that Songbird officially supports, so
why not? Alas, I&#8217;m using Songbird once again. Oh, the nostalgia.
Songbird is good. Still sucks up a fat portion of memory, but shit, most
media players do at this point, and Songbird does more than most and
does it prettier than most. I&#8217;m the last person on earth to give a shit
how much memory things use as long as I have enough to fit what I&#8217;m
working with in, and I have plenty. I&#8217;m an Emacs user. I like the
kitchen sink and all that. Songbird is beautiful:
<img src="http://raynes.me/songbird.png" alt="image" /> The theme (feather) system is
lovely, and Songbird can have virtually any look you can think of. That
isn&#8217;t really what I was looking for though. Media views are important to
me. I am extremely picky about how I am allowed to look at my music. In
the screenshot above, notice there are several different panes. Those
are &#8216;filter panes&#8217;. An alternative to the iTunesy list view, they allow
me to look through my music and narrow things down based on genre,
album, and artist. I&#8217;m actually surprised at how few media players have
something like this. It seemed like a massive miss to me in that new
fangled <a href="http://www.getmiro.com/">Miro</a> thingy and is my top major
reason for not using it. And, if filter view isn&#8217;t enough for you, the
media view stuff in Songbird is extensible! You can actually add new
media views via extensions, so you have an infinite number of ways to
look at your media library and if you&#8217;re a programmer, you can create
them yourself. This brings us to extensions, another neat feature of
Songbird. Songbird is very Mozillia-y, being a descendent of Firefox,
and has a very thorough extension system. When you think of Songbird
extensions, think of Firefox extensions. They&#8217;re quite similar in
capability. Chatzilla actually runs in Songbird, if you need an idea.
Songbird is very modular and a lot of even relatively basic
functionality is implemented as extensions, including the lyrics pane in
my screenshot and even stuff like syncing. And this brings us to
syncing! Songbird is great at syncing. Syncing is implemented as various
extensions for MTP and MSC devices.I use my HTC Evo as my little music
box, so I enjoy easy ways to throw music at it. Android is great because
you can just drag and drop music into the file system just like you
would on your own computer. However, there is something to be said for
being able to sync from your desktop media player just by dragging and
dropping it. This *almost* worked out of the box. Songbird detected my
Evo as soon as I plugged it in. Syncing works out of the box for some
devices because these devices are known and configured beforehand.
Songbird has to know what types of media a device can take and thus it
has to know about that device beforehand for it to work perfectly. If it
doesn&#8217;t know about the device, it has to assume it is just a random
standard media device and only assumes that it can play MP3 files.
Apparently, for some reason, it couldn&#8217;t figure out my poor Evo, and my
music collection mostly consists of OGG files. Jesus wept. So, Songbird
keeps a file in the root of a device&#8217;s file system called
.SBSettings.xml. This file can tell Songbird its capabilities (what it
can play and what it can do). Therefore, it must be changed. I&#8217;m not a
huge fan of editing XML files by hand just to sync music to a phone, so
I found <a href="http://addons.songbirdnest.com/addon/1895/">Devise</a>, a
fantastic little extension for Songbird for editing .SBSettings.xml. It
provides a simple UI for defining your phone&#8217;s capabilities. I got all
that done in about 30 seconds and was syncing music. If you find
yourself in this position and Songbird can&#8217;t figure out your device,
grab that extension. 9 times out of 10, you&#8217;ll spend hours trying to
figure out how to write the XML properly yourself, especially if you
aren&#8217;t a programmer. .SBSettings.xml isn&#8217;t meant to be edited by hand by
users. Beyond this, Songbird does the typical things you&#8217;d expect from a
media player. It actually even has some support for videos, but I&#8217;m not
quite sure how much. If you haven&#8217;t heard much about it in a while, rest
assured that Songbird is still heavily developed and well supported. If
you&#8217;re on OS X or Windows, go ahead and give it a go. If you&#8217;re on
Linux, hell, even you could give it a go. The whole &#8220;we&#8217;ve dropped Linux
support&#8221; thing seems like a case of blowing ones wad a bit too early.
That said, they could break it tomorrow and nobody would have even the
slightest right to bitch about it because they would be able to play the
&#8220;Hey, we called it&#8230;&#8221; card, rightfully. Songbird works on Linux right
now, but it may not work forever. A lot of people just jump shipped and
abandoned Songbird when they dropped Linux support when they could have
saved the whole project if they had just stepped up and helped out. If
you like Songbird, consider helping out with the Linux support.
<a href="http://getnightingale.com/">Nightingale</a> is a step in the right
direction, but they&#8217;re still having trouble getting off the ground.
They&#8217;re keeping people updated but progress is obviously slow, and that
is a direct result of not enough organization and manpower. Help them
out. Nightingale is an important project. P.S. This whole post was meant
to be about syncing Songbird with Android devices, but I&#8217;ll be damned if
I didn&#8217;t turn it into a whole post about Songbird. Hope you enjoyed it
and learned something anyway. :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Seesaw and Tallyho, a match made in heaven]]></title>
    <link href="http://blog.raynes.me/blog/2011/05/16/seesaw-and-tallyho-a-match-made-in-heaven/"/>
    <updated>2011-05-16T01:18:52-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/05/16/seesaw-and-tallyho-a-match-made-in-heaven</id>
    <content type="html"><![CDATA[<p>I do not like swing. Swing is the bane of my existence. As a matter of
fact, none of the various Java GUI frameworks really make me happy. When
I have to use GUI, I always use Swing regardless of my hatred for it,
simply because it is the most accessible of the GUI frameworks. I
haven&#8217;t written many GUI applications in Clojure because avoiding Swing
became a goal. After a week or so of work on a desktop wordpress editor,
I had decided that I would, at some point, write a wrapper around Swing
to make it less insane and painful to use. I&#8217;m very happy to say that
somebody took it upon themselves to do this, and it wasn&#8217;t me. Enter
Seesaw There is a God of a man out there named <a href="http://github.com/daveray">Dave
Ray</a> who is working on a fantastic Swing
wrapper in Clojure called <a href="http://github.com/daveray/seesaw">Seesaw</a>.
Seesaw has a concept of &#8216;widgets&#8217;, where each Swing component is a
widget and a widget is a much more clojury interface to the underlying
component. The README is fantastic at explaining all of this, and I know
that I won&#8217;t do it justice, so just read that. Enter Tallyho My family
and I play a lot of card games. Games like UNO and Quiddler where
scoring is essential and tedious. How much paper have you wasted during
your lifetime just by scoring board games and such alone? Quite a lot,
I&#8217;d bet. I know we have. Recently, we&#8217;ve been using an Android
application for scoring. It works really well and made things a lot
faster and easier. Our pens and papers rejoiced as the war against UNO
ended and they were free. Now I have a laptop. A macbook pro, to be
precise. When we play games, I tend to carry the laptop in with me and
play music and such with it. It only makes sense that I would not crowd
the table with my phone as well just for scoring when I have a laptop in
front of me, but alas, I could find no decent score keepers like the one
I used on Android. I was sad. I sat and pondered my life and the
universe, cried a few tears, blew my nose, and decided that I would just
write one myself. <img src="http://raynes.me/hfiles/tallyho1.png" alt="Tallyho" /> Now
we&#8217;ve already laid down the cold, hard facts, of which there are two:
Swing sucks, Seesaw is awesome. Therefore, it was logicial that I write
Tallyho using Seesaw, and so I did. The result is currently 111 lines of
Clojure (and the majority really is Clojure and looks like Clojure and
not Java!), and is actually pretty nice looking, if simple. There is a
menubar and corresponding right-click menu that all have the same items:
add player, delete player, reset scores, reset all scores. Everything
you need for scoring. The whole main pane is just a giant JTable, a
table with names on the left and scores on the right. To modify a
player&#8217;s score, you double click on a row (a player) and a box pops up
as seen above. You can enter -number to decrement a score or
+number/just the number itself to increment a score. The most shocking
thing about this is how quickly and easily I was able to write this. I
got the basic thing written in 3 hours, and most of that was learning
Seesaw and looking up JTable documentation. It has a good number of
features, looks good, scales good, is cross-platform, stable, and all in
about an hour a day for 3 days. I would have trouble with just
<em>finishing</em> the thing if I had written it in straight Swing. There is
nothing that can make me run screaming away from an application faster
than Swing, but Seesaw made things better. The Code Since the entire
application is weighing in at just 111 lines, I&#8217;ll go ahead and paste
the whole thing, as it is right now, here: [clj] (ns tallyho.core (:use
seesaw.core [clojure.string :only [join]]) (:import [javax.swing
JOptionPane JTable] javax.swing.table.DefaultTableModel
java.awt.event.MouseEvent) (:gen-class)) (def table-model (proxy
[DefaultTableModel] [(to-array-2d []) (object-array [&#8220;name&#8221; &#8220;score&#8221;])]
(isCellEditable [row column] false))) (defn calculate-score [[calc &amp;
digits :as all] old] (let [old (Integer/parseInt (str old)) digits (when
digits (Integer/parseInt (join digits)))] (cond (= \+ calc) (+ old
digits) (= &#45; calc) (- old digits) :else (+ old (Integer/parseInt (join
all)))))) (defn validate [s] (when-not (empty? s) s)) (defn
calc-new-score [old] (when-let [new-score (validate
(JOptionPane/showInputDialog &#8220;Enter -number to decrease score.\nEnter
either +number or number to increase score.&#8221;))] (or (try
(calculate-score new-score old) (catch NumberFormatException e)) (do
(alert &#8220;Enter a real number, dude.&#8221;) (recur old))))) (defn
on-table-click [e] (when (and (= (.getButton e) MouseEvent/BUTTON1) (= 2
(.getClickCount e))) (let [s-table (to-widget e) row (.rowAtPoint
s-table (.getPoint e))] (when (>= row 0) (when-let [new-score
(calc-new-score (.getValueAt s-table row 1))] (.setValueAt table-model
new-score row 1)))))) (declare score-table) (defn add-user [e] (when-let
[user (validate (JOptionPane/showInputDialog &#8220;Enter the player&#8217;s
name.&#8221;))] (.addRow table-model (object-array [user &#8220;0&#8221;])))) (defn
delete-user [e] (when-let [row (selection score-table)] (.removeRow
table-model row))) (defn confirm [] (= JOptionPane/YES_OPTION
(JOptionPane/showConfirmDialog score-table &#8220;Are you sure?&#8221; &#8220;Seriously?&#8221;
JOptionPane/YES_NO_OPTION))) (defn reset-scores [e] (when (confirm)
(doseq [row (range 0 (.getRowCount score-table))] (.setValueAt
table-model 0 row 1)))) (defn reset-game [e] (when (confirm)
(.setRowCount table-model 0))) (def add-user-action (action :handler
add-user :name &#8220;Add Player&#8221;)) (def delete-user-action (action :handler
delete-user :name &#8220;Delete Player&#8221;)) (def reset-scores-action (action
:handler reset-scores :name &#8220;Reset Scores&#8221;)) (def reset-game-action
(action :handler reset-game :name &#8220;Remove All Players&#8221;)) (def
score-table (doto (table :model table-model :listen [:mouse-clicked
on-table-click] :popup (fn [e] [add-user-action delete-user-action
reset-scores-action reset-game-action]) :font &#8220;ARIAL-PLAIN-14&#8221;)
(.setFillsViewportHeight true) (.setRowHeight 20))) (def scroll-pane
(scrollable score-table)) (def menus (menubar :items [(menu :text
&#8220;Tallyho&#8221; :items [add-user-action delete-user-action reset-scores-action
reset-game-action])])) (def main-panel (mig-panel :constraints [&#8220;fill,
ins 0&#8221;] :items [[scroll-pane &#8220;grow&#8221;]])) (defn -main [&amp; args] (invoke-now
(frame :title &#8220;TallyHOOOOOOO&#8221; :content main-panel :on-close :exit
:menubar menus))) [/clj] The project is also on
<a href="https://github.com/Raynes/tallyho">Github</a>. The most amazing part of
this, IMO, is the menus. Look at them! Holy shit! Have you seen Clojure
code that works with JMenus and JMenuBars directly? You *have* to
abstract over it in every application just to be able to look at it and
then look at yourself in the mirror in the morning with pride. With
Seesaw, we have these reusable &#8216;actions&#8217;. We use these actions in the
menubar and the popup menu so almost no code is duplicated or
unnecessary. Then look at the frame. The frame is 5 lines of code and
could fit on 2 or 3. With mig-panel, I can create a mig-layout&#8217;d panel
so quickly and easily that it shouldn&#8217;t even be legal, and that applies
to other layout managers as well. Seesaw is making Swing possible to
read and is getting rid of tedium. At some point, it will be a fairly
comprehensive wrapper. Right now, it is still experimental. It is
experimental because we don&#8217;t really know what we <em>want</em> out of a
Clojure Swing wrapper, and we&#8217;re experimenting to find out. The author
is <strong>very</strong> open to feedback and he absolutely needs it in order for the
project to continue and survive, so please use Seesaw and share your
experiences with him. He is very responsive. The Seesaw project is more
important than I can properly express here. GUI programming is very hard
if you aren&#8217;t familiar with Java and Swing when you come into Clojure,
and even harder to tolerate once you are. Seesaw has the potential to
mitigate this problem effectively, but the community needs to play a
part. Participate! Interested in Tallyho? I imagine that the people who
read this won&#8217;t all be programmers. I also expect some people to end up
here as a result of googling for score keeping apps, and that&#8217;s great. I
hope Tallyho can serve your purpose. If you want to try it out, go to
the project&#8217;s <a href="https://github.com/Raynes/tallyho">Github</a> page, click
the big &#8216;downloads&#8217; button and then download the latest version of
tallyho.jar. This is a standalone jar that you can use. On Windows, you
can typically just double click such a jar for it to run. Otherwise, you
can run it from the command-line by executing the command `java -jar
tallyho.jar`. Usage of the application should be fairly self
explanatory. If you run into any bugs or have any feature requests or
anything like that, create an issue on the github page. Feedback would
be enjoyed.</p>

<p><a href="http://github.com/daveray/seesaw">Seesaw&#8217;s Github page</a> <a href="http://github.com/Raynes/tallyho">Tallyho&#8217;s
Github page</a></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[TryClojure update]]></title>
    <link href="http://blog.raynes.me/blog/2011/04/15/tryclojure-update/"/>
    <updated>2011-04-15T19:30:00-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/04/15/tryclojure-update</id>
    <content type="html"><![CDATA[<p>I&#8217;ve been seeing quite a few
<a href="http://groups.google.com/group/clojure/msg/3c2a21ee61165c24">complaints</a>
about <a href="http://try-clojure.org">TryClojure</a>&#8217;s stability/uptime lately.
Understandably. The uptime of the site has been unbelievably pathetic.
When I started TryClojure, Licenser, the author of its original sandbox,
bought the try-clojure.org domain and hosted the site on his own server
because I didn&#8217;t have anywhere to host it myself. His server used to run
SunOS and now it runs OpenIndia. Both of which I am insanely unfamiliar
with. This limited my ability to maintain the server and such. These
complaints pointed out an obvious problem, and one that I am currently
addressing and want to talk about here. My first step was to move
TryClojure to Alan Malloy&#8217;s and my Linode VPS. As of right now, this
server has been up for over 130 days since the very day we purchased it.
Server uptime will not be a problem. Furthermore, I have the utmost
control over the server and what happens to TryClojure. I can monitor
errors and figure out problems that might make the site go down. A while
back, I did a bit of a <a href="http://groups.google.com/group/clojure/browse_thread/thread/21a0f7c45fb8c112?fwc=1">call to
action</a>
for TryClojure. With the help of the people who participated, TryClojure
was moved to Clojure 1.2, all dependencies updated, a <a href="http://github.com/cognitivedissonance/clojail">new
sandbox</a>, and a fantastic
new design. Unfortunately, some things didn&#8217;t get done, people lost
interest, and once again TryClojure was left in the dust. I&#8217;ve been
fairly busy lately. With the book and general everyday life, my whiny
ass gets a little overwhelmed. Over the past week, I&#8217;ve been <strong>making</strong>
time for TryClojure and I&#8217;ll keep doing that until TryClojure meets its
expectations. My problem is that I&#8217;m just not that good at web
development. TryClojure was my first and is my only serious webdev
project. I can&#8217;t design a website for shit (thankfully, I know people
who can) and I don&#8217;t know JavaScript. When I have to write JavaScript, I
rely on JQuery and JS documentation to get me by. I&#8217;m learning slowly,
but I don&#8217;t use it enough to know it at this point, which is unfortunate
for TryClojure. However, I know people who do know JavaScript, and some
of those people don&#8217;t mind helping out. This brings me to the actual
point of this post. TryClojure is now live on my server with a huge
update.</p>

<ul>
<li>Everybody wanted a TryHaskellish interactive tutorial, well now
you&#8217;ve got one! Thanks to mefesto from #clojure on IRC, we&#8217;ve been
able to get an interactive tutorial up and running.</li>
<li>I&#8217;ve rewritten all the tutorial content and am working on adding
more content to it over the next couple of weeks.</li>
<li>I&#8217;ve got rid of syntax highlighting in the tutorial and in the
console, because it was never all that useful in the first place and
required a jquery-console hack to work that meant I couldn&#8217;t update
jqeury-console without doing that hack over again.</li>
<li>Because of this, I&#8217;ve been able to update jquery-console to the
newest version which allows for copying and pasting! Copying and
pasting was another of the big gripes (once again, understandably).
You can do it now.</li>
<li>Clojail has been updated to the latest version (which is almost
always the same version that sexpbot uses), and I&#8217;ll be updating the
rest of the dependencies (ring and such) to the latest versions
soon.</li>
</ul>


<p>All of these changes are brand new and not guaranteed to be bug free.
I&#8217;m still in the process of putting finishing touches on everything.
Finally, I need to make one last point. Do you know how many TryClojure
bug reports I&#8217;ve received since it was conceived (early last year, I
believe)? Less than 10. Hell, maybe around 5. I don&#8217;t really use it, so
I rarely find these bugs myself. If they are left unreported, I may
never notice them. Remember google groups thread I linked earlier?
That&#8217;s where these issues were raised. When those posts were made,
TryClojure was down. As a matter of fact, that&#8217;s why they were made in
the first place. I did not know that it was down. As a matter of fact, I
didn&#8217;t even see those group posts until Alan mentioned it to me! I was
kind of shocked that they were arguing about stability issues and
TryClojure&#8217;s uptime without even letting me know that it was down in the
first place so that I could figure out the problem. It is really easy to
contact me. I&#8217;m always on IRC on #clojure and #sexpbot, my Github page
is linked from TryClojure&#8217;s about page, and my Github page even has my
email address on it. You can send a telegram if you want, I don&#8217;t care,
just please let me know if something is wrong or else I can&#8217;t do
anything about it. I am open to ideas as to how to make TryClojure
better. If you&#8217;ve got an idea, by all means, pull the <strong>shit</strong> out of
the repository and start implementing stuff. The great thing about open
source is that we can all make TryClojure better together. I can&#8217;t do it
alone.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Return (or, Progress Report on Meet Clojure)]]></title>
    <link href="http://blog.raynes.me/blog/2011/03/22/return-or-progress-report-on-meet-clojure/"/>
    <updated>2011-03-22T10:12:53-05:00</updated>
    <id>http://blog.raynes.me/blog/2011/03/22/return-or-progress-report-on-meet-clojure</id>
    <content type="html"><![CDATA[<p>To start off, I&#8217;ll point out the obvious: I&#8217;ve been all but entirely
inactive for around 2 months. My <a href="http://github.com/Raynes">Github page</a>
does not lie. Alas, I&#8217;ve not written any code. I also haven&#8217;t written
any of my book in the same amount of time. Well, besides maybe two
sections and some various fixes. No serious new content. The fact of the
matter is, I&#8217;m a teenager. I&#8217;m weak. These last two months, I&#8217;ve done
things with my life that didn&#8217;t involve computers, and I enjoyed it. It
was a &#8216;break&#8217; of sorts for me. I have to say I needed it. People who
know me can vouch on the amount of time that I was spending on the
computer for a good year or two. Well, it&#8217;s about time I come back. My
buddy <a href="http://github.com/amalloy">Alan</a> has been maintaining my projects
while I was inactive. Awesome little San Francisconian fellow.
Furthermore, I have a decent shot at getting my book published, so I
really need to get back to work on it. I&#8217;m coming up with a battle plan
that I&#8217;ll detail here. First of all, everything I said about the book is
still true. It&#8217;ll be a totally free introductory text on Clojure even if
I do get it published. Getting it published would just be a huge cherry
at the tip of the ice-cream cone, because it would mean print versions
and a little cash for myself in the process. No Starch seems interested
in my book, and we&#8217;re currently figuring things out. Second, a lot of
the book is written. It is nowhere near done, but I wrote a hefty amount
of tome in a very short amount of time. Of course, speed doesn&#8217;t come
without disadvantages. The book feels rickety to me. I&#8217;m not satisfied
with how everything is right now, so I definitely have a lot of work do
to. The first thing I want to do is read Chris Houser and Mike Fogus&#8217;s
masterpiece, <a href="http://joyofclojure.com/">The Joy of Clojure</a>, from cover
to cover. There are a lot of things that I do not understand and that I
need to understand in order to complete my book. One gaping hole in my
knowledge is about reasonably advanced Java interop and concurrency
stuff. These are things that I&#8217;ll have to spend time with and let sink
in. After I finish The Joy of Clojure, I&#8217;m going to read my entire book
(what is written) from cover to cover and restructure things, add
content where content should be but is missing, and rewrite existing
content that isn&#8217;t <em>quite</em> right. This is hugely important. After that,
I can start adding new content. Furthermore, there are a lot of people
that are interested in reviewing my book. Since the book is going to be
open source, I am not stingy and any Clojurer who would like to help me
out is very welcome to do so. Just email me or send me a message at
Github. If you&#8217;ve already done this before and got no response, try
again. Bug the shit out of me. I don&#8217;t mind. I think I&#8217;ve missed a few
people, so I&#8217;m going to start compiling a list of these people so that I
can keep track of who I should give material to. In conclusion: I&#8217;m
back. You&#8217;ll be able to find me in #clojure more often again. The book
is absolutely still in progress. Slow progress, but progress
nonetheless. I haven&#8217;t left the Clojure game, I&#8217;ve only taken a break. I
love you guys, Clojure, and programming in general way to much to
disappear forever. Sincerely, Anthony <em>P.S. Before you mention it, no,
I&#8217;m not so important that I necessarily need a return announcement. But
damn it, my ego asked for one, and who am I to deny him what he asks
for?</em></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[The Eternal War]]></title>
    <link href="http://blog.raynes.me/blog/2011/02/18/the-eternal-war/"/>
    <updated>2011-02-18T09:44:14-06:00</updated>
    <id>http://blog.raynes.me/blog/2011/02/18/the-eternal-war</id>
    <content type="html"><![CDATA[<p>Editor wars have been going on for many, many years. Many, many years
before my time. Obviously, it isn&#8217;t a war in the traditional sense, but
more of a war of words. Attitude and colorful remarks from both sides of
the war have kept it raging behind the scenes and will continue to do so
for the foreseeable future. Emacs and Vim are two very different text
editors with two very different designs and philosophies. At their core,
they&#8217;re both meant to help users edit text. That is where the
similarities end. Vim is a very lightweight editor. It tries to do one
thing and do it well &#8211; edit text. It does this well and always has.
Emacs is a 500 pound gorilla powerhouse that is designed to do
<em>anything</em> that <em>anybody</em> could ever possibly want. Its extension
language is a Lisp dialect that was created specifically for the editor,
and it&#8217;s quite capable. I think that Vim is a fantastic <em>editor</em>. That
is the goal, right? To be a fantastic editor? It succeeds in what it
sets out to do. Nothing wrong here. But Emacs isn&#8217;t just an editor. As
far as I know, nobody ever claimed that it was a lightweight editor and
that it tries to be what Vim is. It absolutely doesn&#8217;t. When you
experience arguments between Vim and Emacs users, the primary argument
from Vim users is that they want a text editor, not an operating system.
And this is where I begin to get confused. Why is this objectively a bad
thing? Is it a bad thing simply because it isn&#8217;t precisely what Vim
users want? I understand their desire for minimalism, but that is what
<em>they</em> want, and not necessarily what everybody else wants. I don&#8217;t see
how it can be objectively bad for Emacs to not be limited in
functionality. That is not what Emacs is meant to be. Personally, I
enjoy being able to play Tetris, edit text, talk on IRC, read email, and
run a terminal (and possibly even Vim itself from within that terminal)
all within the same application. These are just a few examples of things
that Emacs can be extended to do. A long time ago, how lightweight an
application was was very important due to hardware and space
limitations. Vim is very small, and Emacs is large(r). Some people still
make this argument. It&#8217;s 2011. Please, be serious. 20MB and a bit more
RAM wont make very much of a difference. This is perhaps the silliest
and most irrelevant argument of the bunch. I don&#8217;t have much to say
about it. A useful metaphor is the cell phone. Believe it or not, people
used to purchase those things in order to make phone calls whilst not
near a land-line phone. Shocking, isn&#8217;t it? These days, the making calls
part of phones are perhaps the most irrelevant part of the phone itself.
We are now, quite literally, packing entire operating systems (that are
actually extremely capable) and powerful hardware that, just a few
decades ago, would have been considered magic, into our phones and
distributing them among the people. But they still make phone calls, and
a lot of them do it very well. These phones do a lot. They do things
that are entirely unrelated to making phone calls. I use my cell phone
as a modem and my internet is my phone&#8217;s internet. Phones are also
wildly popular, and the vast majority of people don&#8217;t disregard cell
phones, joke about them, or be hostile towards them simply because they
offer amazing functionality. Some people, however, just want to make
phone calls. People still use home phones. Land-line phones do one
thing, and they do it well. We don&#8217;t throw them away simply because we
have the hottest and most powerful cell phone on the market. This
concept applies to Emacs and Vim. I think they coexist well, and I think
that arguing about which is best is, at best, silly. They&#8217;re different
editors designed for very different things. They have wildly different
philosophies and whichever of those philosophies appeal to you the most
decides which editor you will choose to spend your time with. Most
importantly they both do what they do very well. There are some real and
serious arguments that impacts one&#8217;s choice of editor. Emacs and Vim are
very different when it comes to commands. Emacs uses key chords and Vim
mostly uses words and letters. To close Emacs, you&#8217;d type C-x-C-c or
control+x control+c. In Vim, you&#8217;d switch to command mode and type &#8220;:q&#8221;.
Preference over one or the other often decides which editor someone will
use. I like key chords. I can understand why others may not. In
conclusion, I pose a question: why do we continue to ridicule each
other? I die a little inside whenever I see arguments where there are
jokes like &#8220;I don&#8217;t use Emacs because I only have 1TB of harddrive
space.&#8221; and &#8220;I want a text editor, not an operating system.&#8221;. Wouldn&#8217;t
we be more productive using whatever editor we please to get real work
done rather than arguing about which editor we should use to get said
work done? I think so. To people unfamiliar with the big two, I propose
a piece of advice: try out both of them. Don&#8217;t go off and read
arguments, because those arguments will almost certainly fill your mind
with misconceptions and silliness that need not be there. Just yourself
is all you need to decide which editor <em>you</em> like better. It&#8217;s you that
will be using it, not them. They don&#8217;t have to like it.</p>
]]></content>
  </entry>
  
</feed>
