<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc=" http://purl.org/dc/elements/1.1/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>briancarper.net (λ)</title><link>http://briancarper.net</link><description>Some guy's blog about programming and Linux and cows.</description><item><title>gaka 0.2.0</title><link>http://briancarper.net/blog/562/gaka-020</link><guid>http://briancarper.net/blog/562/gaka-020</guid><pubDate>Thu, 29 Jul 2010 13:59:22 -0700</pubDate><description>&lt;p&gt;Per &lt;a href=&quot;http://briancarper.net/blog/543/introducing-gaka&quot;&gt;many commenters' suggestions&lt;/a&gt; and thanks to code from &lt;a href=&quot;http://github.com/purcell/&quot;&gt;Steve Purcell&lt;/a&gt;, you can now use maps for CSS attributes in &lt;a href=&quot;http://github.com/briancarper/gaka&quot;&gt;gaka&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (println (gaka/css [:a {:color :red}]))
a {
  color: red;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This looks more like vanilla CSS thanks to the curlies, which is nice.   You just have to keep in mind that your key/value pairs could end up being printed in random order, and order is significant&lt;sup id=&quot;fnref:css&quot;&gt;&lt;a href=&quot;#fn:css&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; in CSS.&lt;/p&gt;

&lt;p&gt;It just so happens that maps are implemented in Clojure right now such that if they only have a few entries (16 key/value pairs), the order will be preserved, because you get a &lt;code&gt;PersistentArrayMap&lt;/code&gt; instead of a &lt;code&gt;PersistentHashMap&lt;/code&gt;.  But it's &lt;em&gt;highly dangerous&lt;/em&gt; to rely on such a thing.  It could change at any time in the future.&lt;/p&gt;

&lt;p&gt;In any case, you can also mix and match maps, lists and &quot;flat&quot; keyvals.  They'll all be flattened  That can help preserve attribute order in those cases where you need to.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (println (gaka/css [:a :color &quot;red&quot; {:padding 0} (list :margin 0)]))
a {
  color: red;
  padding: 0;
  margin: 0;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I've also enhanced &quot;mixins&quot; a bit further.  You can now mixin entire tags as well as attributes.  Or a combination of both.  Say you want a mixin that means &quot;&lt;em&gt;Make my element have no padding, and make links within the element be red&lt;/em&gt;&quot;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (println (gaka/css [:div.foo mixin :margin 0]
                         [:div.bar mixin]))
div.foo {
  padding: 0;
  margin: 0;}

  div.foo a {
    color: red;}

div.bar {
  padding: 0;}

  div.bar a {
    color: red;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can get gaka from &lt;a href=&quot;http://github.com/briancarper/gaka&quot;&gt;github&lt;/a&gt; or &lt;a href=&quot;http://clojars.org/gaka&quot;&gt;Clojars&lt;/a&gt;.&lt;/p&gt;&lt;div class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;fn:css&quot;&gt;&lt;p&gt;Order is only significant in cases where you're doing things like &lt;code&gt;padding: 0; padding-left: 1px&lt;/code&gt;.  This is arguably bad CSS style, but it's valid, and it's also possible you'll have this kind of thing if you're generating CSS procedurally.  But most of the time, order is not significant.  e.g. it doesn't matter if you set text color first and background color second, or vice versa.  So maybe this isn't so much of a problem in practice. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:css&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description></item><item><title>Footnotes</title><link>http://briancarper.net/blog/560/footnotes</link><guid>http://briancarper.net/blog/560/footnotes</guid><pubDate>Tue, 27 Jul 2010 12:30:57 -0700</pubDate><description>&lt;p&gt;Did you ever notice how footnotes make your writing seem more important&lt;sup id=&quot;fnref:important&quot;&gt;&lt;a href=&quot;#fn:important&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; somehow?&lt;/p&gt;

&lt;p&gt;Maybe one reason is that &quot;real&quot; books use footnotes.  At a glance, it looks like I have references&lt;sup id=&quot;fnref:refs&quot;&gt;&lt;a href=&quot;#fn:refs&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; backing up everything I say.  In reality, I don't, but the connotation carries through somehow&lt;sup id=&quot;fnref:telepathy&quot;&gt;&lt;a href=&quot;#fn:telepathy&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.  Now my blog seems scholarly and authoritative.&lt;/p&gt;

&lt;p&gt;And if you're like me, you can't resist clicking footnotes to see what they refer to&lt;sup id=&quot;fnref:clickme&quot;&gt;&lt;a href=&quot;#fn:clickme&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;.  According to my estimates, by utilizing footnotes, in one fell swoop I have decreased my readers' average reading efficiency by 73%.&lt;/p&gt;

&lt;p&gt;In any case, I've added experimental, rudimentary support for footnotes to &lt;a href=&quot;http://github.com/briancarper/cow-blog&quot;&gt;cow-blog&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;I'm loosely copying the syntax from &lt;a href=&quot;http://michelf.com/projects/php-markdown/extra/&quot;&gt;Markdown Extra&lt;/a&gt; for this.  Markdown is great, except when it isn't.  The standard doesn't have support for some useful extensions.  I use &lt;a href=&quot;http://attacklab.net/showdown/&quot;&gt;Showdown&lt;/a&gt; for Markdown support, and I'm probably going to work on adding more features of Markdown Extra to Showdown in the near future.&lt;/p&gt;

&lt;p&gt;I just dread actually doing it.  Showdown (like Markdown itself) is implemented as a series of hackish regex transformations of blobs of text.  It's not a proper grammar.  Implementing more of Markdown Extra means more regex blobbing.  It's brittle and fragile and even getting incomplete support for footnotes was less than enjoyable.  But at the same time I find myself wanting to do things that Markdown can't so, so I may have to bite the bullet.&lt;/p&gt;

&lt;p&gt;(If there's a Showdown Extra out there already, drop me a URL.  It'd be most appreciated.  But I couldn't find one.)&lt;/p&gt;&lt;div class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;fn:important&quot;&gt;&lt;p&gt;In reality nothing I say is important. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:important&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&quot;fn:refs&quot;&gt;&lt;p&gt;Does my inner dialog count as a reference? &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:refs&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&quot;fn:telepathy&quot;&gt;&lt;p&gt;Via telepathy. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:telepathy&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&quot;fn:clickme&quot;&gt;&lt;p&gt;See? &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:clickme&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description></item><item><title>Review: Surely You're Joking, Mr. Feynman!</title><link>http://briancarper.net/blog/559/review-surely-youre-joking-mr-feynman</link><guid>http://briancarper.net/blog/559/review-surely-youre-joking-mr-feynman</guid><pubDate>Mon, 26 Jul 2010 16:19:21 -0700</pubDate><description>&lt;p&gt;I'm probably the last person on earth to read &lt;em&gt;&quot;Surely You're Joking, Mr. Feynman!&quot;&lt;/em&gt;, a collection of stories and anecdotes from the life of Richard Feynman.  But better late than never.&lt;/p&gt;

&lt;p&gt;I'll keep this short.  Feynman was the kind of nerd every nerd wishes he was, in one way or another.  He was socially awkward.  He was blunt and tactless.  He felt out of place much of the time.  And it was surprising to see how often Feynman expressed feelings of inadequacy.  He wrote about being highly intimidated when talking in front of big names in physics, and jealous of the math abilities of some other people.&lt;/p&gt;

&lt;p&gt;But none of that stopped him from having an exciting life.  In fact he turned these traits to his advantage.  He had romantic success, often via very highly unconventional means (e.g. walking up to girls and asking them for sex outright; they often said yes).  His bluntness was seen as an admirable trait: where others might be intimidated by a famous physicist, Feynman would give his honest opinion, and that was often appreciated.&lt;/p&gt;

&lt;p&gt;And Feynman went far outside his comfort zone.  He did a stint in biology, even though he knew nothing of biology at the time.  He went to Brazil and joined a samba band.  He sold drawings and paintings for a while.  He played drums for a ballet.&lt;/p&gt;

&lt;p&gt;I picked up at least three lessons from this book.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Try new things, even if you suck at them.  Life is boring if you stick to what you're good at.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be intellectually honest.  Brutally so.  It's the only way to do good science, and arguably the only way to live a good life.  I've always believed this, and Feynman hammers the point home well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even the best and brightest of us feel insecure at times.  You shouldn't let it stop you.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On top of all of that, reading of Feynman's time at Los Alamos working on The Bomb was a fascinating piece of history.  I highly recommend this book.  &lt;/p&gt;

&lt;p&gt;And watch as much of &lt;a href=&quot;http://www.youtube.com/results?search_query=feynman&quot;&gt;Feynman on Youtube&lt;/a&gt; as you can find.  It never ceases to be fascinating.&lt;/p&gt;</description></item><item><title>Review: Logitech Performance MX</title><link>http://briancarper.net/blog/558/review-logitech-performance-mx</link><guid>http://briancarper.net/blog/558/review-logitech-performance-mx</guid><pubDate>Mon, 26 Jul 2010 13:51:27 -0700</pubDate><description>&lt;p&gt;I have a long list of companies I won't buy from, due to horrible customer service experiences or shoddy merchandise.  On the other hand, my &quot;Must buy from this company&quot; list is awfully short.  &lt;/p&gt;

&lt;p&gt;Logitech is one company I'm generally OK buying from.  (For now...)  I go through computer mouses&lt;sup id=&quot;fnref:mouses&quot;&gt;&lt;a href=&quot;#fn:mouses&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; pretty fast, so I end up buying a new one every 4-5 years.  Logitech hasn't failed me thus far.&lt;/p&gt;

&lt;p&gt;In my opinion, the most important parts of your computer are the ones you interact with: Keyboard, mouse, monitor.  I'd rather have a slow computer with a good mouse and keyboard than a fast computer with cheap peripherals.  I already have a &lt;a href=&quot;http://briancarper.net/blog/447/unicomp-customizer-keyboard-review&quot;&gt;wonderful keyboard&lt;/a&gt;, and I like to have a nice mouse to match.&lt;/p&gt;

&lt;p&gt;My latest mouse is the &lt;a href=&quot;http://www.logitech.com/en-us/mice-pointers/mice/devices/5845&quot;&gt;Logitech Performance MX&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/random/performance-mx.png&quot; alt=&quot;Logitech Performance MX&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;!--more Read the whole review --&gt;

&lt;p&gt;How much can you really improve upon the concept of a mouse?  It's a round thing you slide around and click.  Well, if you believe the marketing on the box, it seems there are at least three areas for improvement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add more buttons.&lt;/li&gt;
&lt;li&gt;Make it work on more surfaces.&lt;/li&gt;
&lt;li&gt;Gimmicks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of the above features matter much to me.  What I care about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is it comfortable to use all day?&lt;/li&gt;
&lt;li&gt;Is it going to last me 4 or 5 years?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's hit each of these points.&lt;/p&gt;

&lt;h1&gt;More buttons&lt;/h1&gt;

&lt;p&gt;The Performance MX has a lot of buttons.  If you believe &lt;code&gt;xev&lt;/code&gt;, there are somewhere between 10 and 13 (counting the scroll wheel etc.).  &lt;/p&gt;

&lt;p&gt;I don't need all of these.  I like to have a thumb button for middle-clicking, but this mouse has four of them.  What am I going to use all of the extras for?  Maybe I'll have one open Emacs.&lt;/p&gt;

&lt;p&gt;The browser forward and back thumb buttons work out-of-the-box in Linux, which surprised me.  I'm used to having to dork around with &lt;code&gt;xmodmap&lt;/code&gt; or &lt;code&gt;xbindkeys&lt;/code&gt;.  Linux has come a long way.  &lt;/p&gt;

&lt;p&gt;Then there's a thumb button that's supposed to open Expose on OS X or your app-switcher of choice in your OS.  It's in a place that's impossible to press consistently with your thumb, unless you're a contortionist.  And it does nothing in Linux by default.  Similarly there's a vanishingly small &quot;zoom&quot; button near your thumb.  I can't even find this button without looking at the mouse, because it's too small.&lt;/p&gt;

&lt;p&gt;There's also horizontal scroll, if you can manage to tilt the wheel to either side.  My fingers lack the dexterity to do this easily, and the button is awfully stiff, so I'll never use this.&lt;/p&gt;

&lt;p&gt;But hey, better too many buttons than too few.&lt;/p&gt;

&lt;h1&gt;Surfaces&lt;/h1&gt;

&lt;p&gt;The marketers wrote &quot;This mouse works on glass!&quot; all over the box, so this must be a selling point of the mouse.  But my Logitech G500 also worked on glass, once I upgraded the firmware (via Logitech's own website), so I'm not sure why this feature is being pushed so hard.&lt;/p&gt;

&lt;h1&gt;Gimmicks&lt;/h1&gt;

&lt;p&gt;On the Performance MX, you can click this little switch and disengage part of the mechanism that makes the scroll wheel &quot;click&quot;.  This lets you spin the wheel hard and it'll spin freely forever until you stop it manually, or until its inertia runs out.&lt;/p&gt;

&lt;p&gt;Do I really need, and I quote, &lt;strong&gt;hyper-fast scrolling&lt;/strong&gt;?  The scroll speed of my mouse wheel has never struck me as a productivity bottleneck.  Now that I've played with it, sure, it's mildly interesting.  If you scroll at just the right speed, you can go all the way from the top of a Reddit thread to the bottom, while still skimming the text.  But I don't need this feature.&lt;/p&gt;

&lt;p&gt;But the levels to which marketroids will stoop to hype up a product never ceases to amaze me.  The name itself is ridiculous.  &lt;em&gt;Performance MX.&lt;/em&gt;  Who comes up with this?&lt;/p&gt;

&lt;h1&gt;Comfort&lt;/h1&gt;

&lt;p&gt;My previous mouse was a G500, and it was really quite big.  A bit too big.  (And there were these removable weights you could use to adjust the weight of the mouse (the &quot;gimmick&quot; feature of that mouse), which were worthless.)&lt;/p&gt;

&lt;p&gt;The MX is bigger, but it has more curves and that helps my hand sit on it better.  There's a very large thumb rest, which I like.  If you have small hands, you might have problems with this mouse.  I like the feel so far.&lt;/p&gt;

&lt;h1&gt;Will it last?&lt;/h1&gt;

&lt;p&gt;This mouse feels very solid.  It has a nice heft to it.    It has metal bits (or sturdy metal-like plastic) in places other mouses have cheap plastic and rubber.&lt;/p&gt;

&lt;p&gt;The wheel on my G500 wore down very fast.  I was some kind of rubbery plastic, which became rounded off, nicked and worn-down a lot over the years.   The wheel on the MX feels like some kind of metal, with nice ridges on the top to provide friction.  I'm a bit concerned that the wheel disengagement switch might wear down over time though.  The more moving parts, the more opportunity for something to break.&lt;/p&gt;

&lt;p&gt;The pads on the bottom are huge: a good 1 inch x 1/8 inch.  I love this.  Using this on my Icemat mouse pad is very comfy.  It slides effortlessly, but leaves me with plenty of control.&lt;/p&gt;

&lt;h1&gt;Battery&lt;/h1&gt;

&lt;p&gt;I normally prefer corded mouses.  This one is rechargeable.  Most rechargeable mouses I've used had some kind of stupid dock you had to jam the mouse into.  Not only does it take up desk space, it also wears holes into your mouse over time from being inserted and removed from the dock.  The battery connectors also wear down fast, and then it doesn't charge right unless you fiddle with it.&lt;/p&gt;

&lt;p&gt;The MX can be charged while you use it, via a USB cable, which is a really good idea.  It comes with a short cable, and a long extender cable if you need it.  The cable looks like it has a non-standard connector though, which is a bad idea.  Why not use mini-USB?&lt;/p&gt;

&lt;p&gt;The charger cable is awfully heavy though.  I wouldn't want to use this thing while it's plugged in for more than a short time.&lt;/p&gt;

&lt;p&gt;I don't know what the battery life is like, because it hasn't run out yet, after a couple days.  That's good enough for me.&lt;/p&gt;

&lt;h1&gt;Price&lt;/h1&gt;

&lt;p&gt;$120.  That's a lot.  But taken over 4-5 years, it's not that bad.&lt;/p&gt;

&lt;p&gt;And the surgery I'd have to have done on my hands after crippling them via a cheap mouse over the next 40 years would be way more than $120.&lt;/p&gt;

&lt;h1&gt;Should you buy this?&lt;/h1&gt;

&lt;p&gt;Sure.  I like this mouse so far.  It works, and it feels like it'll last a while.  Props to Logitech for making good products.&lt;/p&gt;&lt;div class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;fn:mouses&quot;&gt;&lt;p&gt;Yes, &lt;a href=&quot;http://en.wikipedia.org/wiki/Mouse_%2528computing%2529#Naming&quot;&gt;mouses&lt;/a&gt;. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:mouses&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description></item><item><title>Clojure syntax highlighting via SyntaxHighlighter</title><link>http://briancarper.net/blog/556/clojure-syntax-highlighting-via-syntaxhighlighter</link><guid>http://briancarper.net/blog/556/clojure-syntax-highlighting-via-syntaxhighlighter</guid><pubDate>Wed, 21 Jul 2010 15:17:42 -0700</pubDate><description>&lt;p&gt;How do you syntax-highlight Clojure code for display on a website?   The best way I can find is &lt;a href=&quot;http://alexgorbatchev.com/SyntaxHighlighter/&quot;&gt;SyntaxHighlighter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Daniel Gómez wrote a &lt;a href=&quot;http://www.deepbluelambda.org/programs/sh-clojure/new-clojure-brush-for-syntaxhighlighter&quot;&gt;brush&lt;/a&gt; to give SyntaxHighlighter Clojure support.  I &lt;a href=&quot;http://github.com/briancarper/cow-blog/blob/master/public/js/brushes/shBrushClojure.js&quot;&gt;tweaked it&lt;/a&gt; a bit myself and integrated it into cow-blog.  I also converted my &lt;a href=&quot;http://github.com/briancarper/dotfiles/blob/master/.vim/colors/gentooish.vim&quot;&gt;favorite color scheme&lt;/a&gt; to a &lt;a href=&quot;http://github.com/briancarper/cow-blog/blob/master/public/css/shThemeCow.css&quot;&gt;SyntaxHighlighter theme&lt;/a&gt;.  So when I write this code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;(defn- ip
  &quot;Given a request, return the IP.  Looks for an x-forwarded-for
  header, falls back to :remote-addr on the request.&quot;
  [request]
  (or (get-in request [:headers &quot;x-forwarded-for&quot;])
      (request :remote-addr)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/clojure/syntax-highlight.png&quot; alt=&quot;Syntax highlighting example&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;...unless you're reading this via RSS, or in a browser without Javascript enabled, in which case you'll see plain, depressing black and white.  But that's one nice thing about SyntaxHighlighter.  It degrades nicely.&lt;/p&gt;

&lt;p&gt;One bad thing about SyntaxHighlighter is that it doesn't play nicely with Markdown.  Or rather, Markdown isn't powerful enough to let you specify the class of any markdown-generated HTML tags.  If you want the &lt;code&gt;&amp;lt;pre class=&quot;brush: clojure&quot;&amp;gt;&lt;/code&gt; that SyntaxHighlighter requires, you have to write out the HTML by hand.  But I hacked Showdown a bit to let me specify classes more easily, so I can avoid having to do that.&lt;/p&gt;

&lt;p&gt;The code for all of this is &lt;a href=&quot;http://github.com/briancarper/cow-blog&quot;&gt;on github&lt;/a&gt; with the rest of my blog.&lt;/p&gt;</description></item><item><title>ANTLR via Clojure</title><link>http://briancarper.net/blog/554/antlr-via-clojure</link><guid>http://briancarper.net/blog/554/antlr-via-clojure</guid><pubDate>Fri, 16 Jul 2010 17:41:04 -0700</pubDate><description>&lt;p&gt;&lt;a href=&quot;http://www.antlr.org/&quot;&gt;ANTLR&lt;/a&gt; is a parser-generator for Java.  Can you use it from Clojure?  Sure.  Would you want to?  Maybe.&lt;/p&gt;

&lt;p&gt;Here's how to do it, start to finish.&lt;/p&gt;

&lt;p&gt;For the impatient among you, all of the code below is on &lt;a href=&quot;http://github.com/briancarper/clojure-antlr-example&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/briancarper/clojure-antlr-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;!--more Grammars, grammars, everywhere --&gt;

&lt;h1&gt;Setup&lt;/h1&gt;

&lt;p&gt;I'm going to use leiningen for this project.  Let's make a new project called &lt;code&gt;antlr-example&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ lein new antlr-example
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now edit &lt;code&gt;project.clj&lt;/code&gt; to tell lein to fetch ANTLR (and Swank, since I use Emacs).  ANTLR is available in Maven Central, so leiningen can grab it for us.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defproject antlr-example &quot;1.0.0-SNAPSHOT&quot;
  :description &quot;ANTLR Clojure example&quot;
  :dependencies [[org.clojure/clojure &quot;1.2.0-beta1&quot;]
                 [org.clojure/clojure-contrib &quot;1.2.0-beta1&quot;]
                 [org.antlr/antlr &quot;3.2&quot;]]
  :dev-dependencies [[swank-clojure &quot;1.3.0-SNAPSHOT&quot;]])
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then a simple &lt;code&gt;lein deps&lt;/code&gt; will download all of the jars and all of ANTLR's dependencies for you.  Handy.  You end up with this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;antlr-example
├── classes
├── lib
│   ├── antlr-2.7.7.jar
│   ├── antlr-3.2.jar
│   ├── antlr-runtime-3.2.jar
│   ├── clojure-1.2.0-beta1.jar
│   ├── clojure-contrib-1.2.0-beta1.jar
│   ├── stringtemplate-3.2.jar
│   └── swank-clojure-1.3.0-20100502.112537-1.jar
├── project.clj
├── README
├── src
│   └── antlr_example
│       └── core.clj
└── test
    └── antlr_example
        └── core_test.clj
&lt;/code&gt;&lt;/pre&gt;

&lt;h1&gt;Interactive development?  Kind of...&lt;/h1&gt;

&lt;p&gt;One weakness of ANTLR via Clojure is that development is no longer REPL-based.  The ANTLR workflow is essentially a Java workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a grammar&lt;/li&gt;
&lt;li&gt;Compile the grammar into Java source code&lt;/li&gt;
&lt;li&gt;Compile the Java source into Java .class files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Until and unless someone writes a Clojure backend for ANTLR, so that ANTLR can directly produce Clojure source code (and it seems like it should be possible to do so), you're back to a write/compile/debug cycle.  Joy!&lt;/p&gt;

&lt;p&gt;This also means restarting your REPL every time you alter and recompile your grammar.&lt;/p&gt;

&lt;p&gt;But the good thing is that there's &lt;a href=&quot;http://antlr.org/works/index.html&quot;&gt;ANTLR Works&lt;/a&gt;, a free GUI for writing ANTLR grammars.  ANTLR Works has an interactive interpreter, which is nice, and it has a compiler/debugger, which is better.  These let you test your grammar as you write it, by trying inputs and seeing the resulting AST in graphical form, which is as good as you could hope for.  This is actually even a bit nicer than a plaintext REPL.  &lt;/p&gt;

&lt;p&gt;Plus, it has a nice editor for ANTLR code.  Emacs was freaking out over the indentation on a few grammars I tried.&lt;/p&gt;

&lt;p&gt;So a decent workflow might be to write and debug your grammar in ANTLR Works, then fire up Clojure afterwards to consume the parser.&lt;/p&gt;

&lt;h1&gt;A simple grammar&lt;/h1&gt;

&lt;p&gt;We're going to use a classic textbook example, simple arithmetic expressions, as a proof of concept.&lt;/p&gt;

&lt;p&gt;For Java (hence Clojure) to find this code, it has to be named properly and it has to be put into the proper directory on &lt;code&gt;CLASSPATH&lt;/code&gt;.  I'm going to create the grammar file &lt;code&gt;src/antlr_example/Expr.g&lt;/code&gt;.  (My grammar file, Java source, and Clojure source will all be jumbled together in &lt;code&gt;src&lt;/code&gt;.   You can easily do it differently if this offends your sensibilities.)&lt;/p&gt;

&lt;p&gt;The first line in the grammar names the grammar (and the classes we'll consume from Clojure):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;grammar Expr;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Clojure being a s-exp based language, it might be nice to have ANTLR generate an AST.  ANTLR has good support for this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;options {
    output=AST;
    ASTLabelType=CommonTree;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Helper tokens for the grammar:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;tokens {
    PLUS = '+';
    MINUS = '-';
    MULT = '*';
    DIV = '/';
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These next lines are important.  We want to generate classes &lt;code&gt;antlr_example.ExprLexer&lt;/code&gt; and &lt;code&gt;antlr_example.ExprParser&lt;/code&gt;, so we have to set our code up in the proper package, &lt;code&gt;antlr_example&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;This code includes both a parser and lexer, so you have to set the package for both in ANTLR.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@header        {package antlr_example;}
@lexer::header {package antlr_example;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then the grammar itself, which is simple:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;expr: term ((PLUS | MINUS)^ term)* ;
term: factor ((MULT | DIV)^ factor)* ;
factor: INT ;

INT :   '0'..'9'+ ;
WS  :   ( ' ' | '\t' | '\r' | '\n')+ {$channel=HIDDEN;} ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;One thing to note is &lt;code&gt;(PLUS | MINUS)^&lt;/code&gt;; the &lt;code&gt;^&lt;/code&gt; here tells ANTLR to treat &lt;code&gt;(PLUS | MINUS)&lt;/code&gt; as the parent of the node created for this rule.  This means the &lt;code&gt;term&lt;/code&gt; on either side will be children of this node.  We do the same for &lt;code&gt;MULT&lt;/code&gt; and &lt;code&gt;DIV&lt;/code&gt;.  This will give us a nice tree.  Otherwise you end up with a flat list of tokens.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{$channel=HIDDEN;}&lt;/code&gt; for the whitespace rule tells ANTLR to ignore whitespace, also useful.&lt;/p&gt;

&lt;p&gt;If you use ANTLR Works with this grammar, on the input &lt;code&gt;1 + 2 * 3 + 4&lt;/code&gt;, you can see that it works OK.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/random/antlr.png&quot; alt=&quot;ANTLR&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That was an awful lot of boilerplate and setup and ugly syntax though.  See how much Clojure spoils you?  But for a simple grammar, it's not that much code.&lt;/p&gt;

&lt;p&gt;For a longer or more complex grammar, you might end up having to embed inline Java code into your ANTLR grammar.  But again, that's not the end of the world.&lt;/p&gt;

&lt;h1&gt;Compile everything&lt;/h1&gt;

&lt;p&gt;Once you write your grammar (presumably in ANTLR Works), you can generate the Java code and compile it thusly (run from your project's base directory):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ java -cp 'lib/*' org.antlr.Tool src/antlr_example/Expr.g
$ javac -cp 'lib/*' -d classes src/antlr_example/Expr{Lexer,Parser}.java
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first command should generate &lt;code&gt;src/antlr_example/ExprLexer.java&lt;/code&gt; and &lt;code&gt;src/antlr_example/ExprParser.java&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second command should spew tons of class files into &lt;code&gt;classes/&lt;/code&gt;.  Leiningen and other tools generally include &lt;code&gt;classes/&lt;/code&gt; on your &lt;code&gt;CLASSPATH&lt;/code&gt;, so that's a good place for them.  Again putting them here is a fairly arbitrary decision on my part.  You can put the source and class files anywhere, as long as the class files end up on your &lt;code&gt;CLASSPATH&lt;/code&gt; when you start Clojure.&lt;/p&gt;

&lt;p&gt;If you're really so lazy that you can't run these two commands, you could use ANTLR Works' built-in commands for compiling your grammar.  The only way I could find to invoke it was by starting a Debug session, which compiled my code as a side-effect.  And it spews files into places I don't want them, so I like the command line version better.&lt;/p&gt;

&lt;p&gt;You could probably also set up a leiningen task for this if you're re-running these commands a lot, or configure your build tool of choice to do the same.  I didn't bother.&lt;/p&gt;

&lt;p&gt;Now (re)start your REPL and let's write some Clojure.&lt;/p&gt;

&lt;h1&gt;Clojure code&lt;/h1&gt;

&lt;p&gt;It's pretty easy to consume an ANTLR parser from Clojure.  I'm putting the code below into &lt;code&gt;src/antlr_example/core.clj&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;First import the classes.  You probably need some ANTLR helper classes to set up the parser; you also need the classes you just generated.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(ns antlr-example.core
  (:import (org.antlr.runtime ANTLRStringStream
                              CommonTokenStream)
           (antlr_example ExprLexer ExprParser)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here's a function to parse a string using our new parser class:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn parse-expr [s]
  (let [lexer (ExprLexer. (ANTLRStringStream. s))
        tokens (CommonTokenStream. lexer)
        parser (ExprParser. tokens)]
    (.expr parser)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;.expr&lt;/code&gt; is the name of the top-level rule we want to invoke from our grammar.  The rest is boilerplate; just plug in the proper class names.&lt;/p&gt;

&lt;p&gt;So let's see what we've got.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;user&amp;gt; (in-ns 'antlr-example.core)
#&amp;lt;Namespace antlr-example.core&amp;gt;
antlr-example.core&amp;gt; (parse-expr &quot;1 + 2 * 3 + 4&quot;)
#&amp;lt;expr_return antlr_example.ExprParser$expr_return@3755e508&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Er, OK.  A good way to inspect Java objects is via &lt;code&gt;bean&lt;/code&gt;, so let's view the guts of this object:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;antlr-example.core&amp;gt; (bean *1)
{:tree #&amp;lt;CommonTree +&amp;gt;,
 :template nil,
 :stop #&amp;lt;CommonToken [@12,12:12='4',&amp;lt;8&amp;gt;,1:12]&amp;gt;,
 :start #&amp;lt;CommonToken [@0,0:0='1',&amp;lt;8&amp;gt;,1:0]&amp;gt;,
 :class antlr_example.ExprParser$expr_return}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I see.  We set up our grammar above to generate an AST, so &lt;code&gt;:tree&lt;/code&gt; on the bean will give us that.  This translates to &lt;code&gt;.getTree&lt;/code&gt; on the Java object.  So we can edit our function to call this for us, since we always want to have the tree.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn parse-expr [s]
  (let [lexer (ExprLexer. (ANTLRStringStream. s))
        tokens (CommonTokenStream. lexer)
        parser (ExprParser. tokens)]
    (.getTree (.expr parser))))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;antlr-example.core&amp;gt; (parse-expr &quot;1 + 2 * 3 + 4&quot;)
#&amp;lt;CommonTree +&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OK, it appears we have one node of the tree, the root node.  Let's peak inside:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;antlr-example.core&amp;gt; (bean *1)
{:children #&amp;lt;ArrayList [+, 4]&amp;gt;,
 :childIndex -1,
 :parent nil,
 :text &quot;+&quot;,
 :nil false,
 :token #&amp;lt;CommonToken [@10,10:10='+',&amp;lt;4&amp;gt;,1:10]&amp;gt;,
 :class org.antlr.runtime.tree.CommonTree,
 :ancestors nil,
 :tokenStartIndex 0,
 :type 4,
 :childCount 2,
 :charPositionInLine 10,
 :tokenStopIndex 12,
 :line 1}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Looks like our children are in &lt;code&gt;:children&lt;/code&gt; on the bean, which translates to &lt;code&gt;.getChildren&lt;/code&gt; on the Java object.  And we have &lt;code&gt;.getChildCount&lt;/code&gt; to count the children, and &lt;code&gt;.getText&lt;/code&gt; to get the text of the current node.  (We could've learned all of this by reading the javadocs for ANTLR too, but what fun is that?)&lt;/p&gt;

&lt;p&gt;Since we're dealing with a tree, we can use &lt;code&gt;tree-seq&lt;/code&gt; in Clojure to get a flat list of all the tokens in our text.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;tree-seq&lt;/code&gt; takes three arguments.  First a function that returns true if the node has children, false otherwise.  Second a function that returns the children for a node that has children.  Third the root node of our tree.&lt;/p&gt;

&lt;p&gt;That'll give us a seq of tree nodes.  So finally we call &lt;code&gt;.getText&lt;/code&gt; on all the resulting nodes in the list, to turn node objects into strings.&lt;/p&gt;

&lt;p&gt;Easy enough:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn node-seq [x]
  (map #(.getText %)
   (tree-seq #(not (zero? (.getChildCount %)))
             #(.getChildren %)
             x)))

antlr-example.core&amp;gt; (node-seq (parse-expr &quot;1 + 2 * 3 + 4&quot;))
(&quot;+&quot; &quot;+&quot; &quot;1&quot; &quot;*&quot; &quot;2&quot; &quot;3&quot; &quot;4&quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But that's no good.  We lost our tree structure.  &lt;/p&gt;

&lt;p&gt;We'd rather have something like nested vectors or lists.  It's easy enough to roll something by hand.  This should do it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn AST [node]
  (if (zero? (.getChildCount node))
    (.getText node)
    (let [children (map AST (.getChildren node))
          txt (.getText node)]
      (if txt
        (cons txt children)
        children))))

antlr-example.core&amp;gt; (AST (parse-expr &quot;1 + 2 * 3 + 4&quot;))
(&quot;+&quot; (&quot;+&quot; &quot;1&quot; (&quot;*&quot; &quot;2&quot; &quot;3&quot;)) &quot;4&quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's better, but it's a list of strings.  These strings all happen to be literal representations of Clojure objects, so a call to &lt;code&gt;read-string&lt;/code&gt; in the proper places should give us something we can work with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn AST [node]
  (if (zero? (.getChildCount node))
    (read-string (.getText node))
    (let [children (map AST (.getChildren node))
          txt (read-string (.getText node))]
      (if txt
        (apply list txt children)
        children))))

antlr-example.core&amp;gt; (AST (parse-expr &quot;1 + 2 * 3 + 4&quot;))
(+ (+ 1 (* 2 3)) 4)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hey look, now we have something we can evaluate.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;antlr-example.core&amp;gt; (eval *1)
11
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Yeah I kind of planned that ahead.  If you had a more complex grammar, you might not get away with something quite this easy.&lt;/p&gt;

&lt;h1&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;The advantage of ANTLR is that it's mature, widely used, actively developed, and well-documented.  (There's a whole book about ANTLR, &lt;em&gt;The Definitive ANTLR Reference&lt;/em&gt; by Terence Parr.)  There's also a lot of tooling available for it, not just ANTLR Works, but plugins for other Java IDEs.&lt;/p&gt;

&lt;p&gt;The disadvantage is that it's a Java library, and as always, there will be some friction when consuming it from Clojure.  But it's not that bad.&lt;/p&gt;

&lt;p&gt;For pure-Clojure parser generator alternatives, there are &lt;a href=&quot;http://github.com/joshua-choi/fnparse&quot;&gt;fnparse&lt;/a&gt; and &lt;a href=&quot;http://github.com/cypher/clojure-pg&quot;&gt;clojure-pg&lt;/a&gt;, neither of which I've tried much.&lt;/p&gt;</description></item><item><title>Clementine: A triumph of Free Software</title><link>http://briancarper.net/blog/553/clementine-a-triumph-of-free-software</link><guid>http://briancarper.net/blog/553/clementine-a-triumph-of-free-software</guid><pubDate>Tue, 13 Jul 2010 11:48:51 -0700</pubDate><description>&lt;p&gt;Ages ago, in the long-forgotten days of 2008, there was Amarok 1.4.  And it was good.  Then KDE4 came along and Amarok was rewritten, reshaped, becoming something... different.  Something unsettling.  Something &lt;a href=&quot;http://briancarper.net/blog/463/songbird-vs-amarok-how-not-to-design-a-gui&quot;&gt;not&lt;/a&gt; &lt;a href=&quot;http://briancarper.net/blog/516/exaile-the-best-amarok-since-amarok-14&quot;&gt;altogether&lt;/a&gt; &lt;a href=&quot;http://briancarper.net/blog/494/amarok-22-disber-grogth-grocks&quot;&gt;pleasant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fear not.  Today we have &lt;a href=&quot;http://www.clementine-player.org/&quot;&gt;Clementine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/random/clementine-0.4.png&quot;&gt;&lt;img src=&quot;/random/thumbs/clementine-0.4.png&quot; alt=&quot;Clementine&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I consider Clementine a triumph of Free Software.  A great project fell off the rails, so someone else picked up the pieces, forked it and kept the spirit alive.&lt;/p&gt;

&lt;!--more Gushing praise follows--&gt;

&lt;h1&gt;Features present&lt;/h1&gt;

&lt;p&gt;Clementine embodies everything good about Amarok 1.4, in a shiny Qt4 package.  The layout is eminently pleasant to use.  It uses the classic &quot;spreadsheet&quot; playlist view that saw so much success in Amarok 1.4.  If you care about cramming as much information about your music as possible onto the screen, this is as good as it gets.  It's boring, and that's a good thing.  It gets the job done.&lt;/p&gt;

&lt;p&gt;Like Amaork, 1.4, in Clementine you can very quickly drill into your music collection, filter it, view recently added tracks, group songs by artist or album or year or genre or a combination of those things.  Clementine also handles all of the edge cases correctly: it lists albums with Various Artists exactly how I'd want (exactly like Amarok 1.4).  It correctly handles songs with non-Latin tag text.&lt;/p&gt;

&lt;p&gt;Clementine detects additions and changes to my music collection instantly, without the massive scan-lags on startup that plague some other music players.  Clementine doesn't bat an eye at my 7,000 song collection.  There's no MySQL integration, but I don't need it.  Clementine's SQLite backend supposedly handles 300k songs without much problem, which is good enough for me.&lt;/p&gt;

&lt;p&gt;Clementine has Last.FM integration.  It has three different styles of desktop notification.  It has visualizations.  It handles USB devices.  It understands reply gain.  It has cross-fading.  It has an equalizer.  It has a transcoder.  It has a cover manager.&lt;/p&gt;

&lt;p&gt;I'm tired of listing features.  Let's just say it has every &lt;em&gt;useful&lt;/em&gt; feature you'd ever want.  And if you don't need a feature, it stays out of your way.&lt;/p&gt;

&lt;p&gt;And for a program under such active development, it's rock solid.  I have yet to see a crash.  And speaking of active development, if you follow the activity in Clementine's SVN repo, you will find that this program is updated almost daily.  How the devs find the time, I don't know, but I'm grateful.  This program has gone from non-existent to awesome in record time.&lt;/p&gt;

&lt;p&gt;Clementine can use gstreamer, so it even works cross-platform.  I fired it up on Windows 7 the other day and I was amazed at how good it looked and felt.  It supposedly also works on OS X.&lt;/p&gt;

&lt;p&gt;Clementine doesn't cook your breakfast for you, but that might be in the works.&lt;/p&gt;

&lt;h1&gt;How to make a good UI&lt;/h1&gt;

&lt;p&gt;A perfect example of the polish of Clementine's UI: Tagging.  How do you tag a whole album worth of music at once?  You can select some songs and right click and go into a dialog, like most music players allow.  &lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Edit a tag for a single song (inline) by clicking the field.  Let's say you edit &lt;em&gt;Artist&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Select multiple songs in your playlist.  (Click and drag, CTRL-click, Shift-click, CTRL-A, whatever.)&lt;/li&gt;
&lt;li&gt;Right click the &lt;em&gt;Artist&lt;/em&gt; tag in the song you edited, select &lt;em&gt;Set Artist to &quot;XXXXX&quot;&lt;/em&gt;, and now all the songs you selected will be updated.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/random/clementine-0.4_2.png&quot; alt=&quot;Clementine&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is the kind of UI innovation that I like.  It's simple, it's useful, and it's predictable.  You can get things done without going through dialog windows, without a million clicks, without spending a minute scratching your head figuring things out.&lt;/p&gt;

&lt;p&gt;(Meanwhile Amarok 2 is busy getting rid of the Stop button and making the volume control circular.)&lt;/p&gt;

&lt;h1&gt;Features missing&lt;/h1&gt;

&lt;p&gt;Admittedly, Clementine is missing a couple of features I wouldn't mind having.  You can't skin or theme Clementine.  You can't rate songs.  You can't display song lyrics.  You can't &quot;queue&quot; songs.  But oh well.  I can live without these features because the rest of the program is so darned good.   For all I know, these features might pop up next week.  I wouldn't be surprised.&lt;/p&gt;

&lt;p&gt;The Clementine devs seem to be very friendly and responsive to feature requests and feedback, which is also great.&lt;/p&gt;

&lt;p&gt;Clementine is also missing a few features/bloat that I'm glad to see NOT ported from Amarok.  Wikipedia integration?  Good riddance.&lt;/p&gt;

&lt;h1&gt;I would pay money for this program.&lt;/h1&gt;

&lt;p&gt;In November 2009 I had &lt;a href=&quot;http://briancarper.net/blog/494/amarok-22-disber-grogth-grocks&quot;&gt;this to say&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;(Anyone out there reading this, if you port Amarok 1.4 to Qt4 intact, I will pay you. Seriously. I will pay you money.)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The offer still stands.  I will pay money for Clementine.  I'm &lt;a href=&quot;http://groups.google.com/group/clementine-player/browse_frm/thread/e04fc078e80724f0/7b32edc7ece2f762&quot;&gt;still waiting&lt;/a&gt; for a Donate link so I can do so.  (Clementine devs, are you reading this?)&lt;/p&gt;

&lt;p&gt;Why do I care about this so much?  Because I have music playing whenever I'm using this computer, and when you add up work plus free time, I'm at this computer 8-10 hours per day.  Music keeps me sane during multi-hour debug sessions.  Music is an integral part of my life, and a music app is an integral part of playing music.&lt;/p&gt;

&lt;p&gt;It's very important to me that the programs and tools I use all day are comfortable.  Otherwise I become cranky.  If you were a carpenter, would you want to use a hammer with a wobbly handle all day?  I'm a programmer, and I want to use comfortable computer programs.&lt;/p&gt;

&lt;p&gt;Clementine is very comfortable.&lt;/p&gt;</description></item><item><title>Vim and plaintext data files</title><link>http://briancarper.net/blog/552/vim-and-plaintext-data-files</link><guid>http://briancarper.net/blog/552/vim-and-plaintext-data-files</guid><pubDate>Mon, 12 Jul 2010 10:42:48 -0700</pubDate><description>&lt;p&gt;I use Vim to work with plaintext datasets.  Here are some habits and code snippets I've picked up which make data files a bit easier to deal with.&lt;/p&gt;

&lt;!--more Read on for Vim trickery...--&gt;

&lt;h1&gt;Tab-delimited files&lt;/h1&gt;

&lt;p&gt;If you have a file full of tab-separated values, the columns may not line up very well due to the variable display-length of tabs.  You'll often end up with something that looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo bar
longvaluehere    quux
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But things will line up if you set tabstop to a value greater than the longest column.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:setlocal tabstop=16
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then it'll look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo             bar
longvaluehere   quux
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can use visual-block mode to add and remove columns easily.&lt;/p&gt;

&lt;p&gt;If you're working with TSV files, you'll want to set the &lt;code&gt;listchars&lt;/code&gt; option in such a way that tabs are displayed specially.  I use this, stolen/adapted from &lt;a href=&quot;http://github.com/ciaranm/dotfiles-ciaranm/blob/master/vimrc&quot;&gt;here&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:set listchars=eol:\ ,tab:»-,trail:·,precedes:…,extends:…,nbsp:‗
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remember also that you can insert a literal tab into your file no matter what you have &lt;code&gt;expandtab&lt;/code&gt; set to, by hitting &lt;code&gt;CTRL-v&lt;/code&gt; first.  &lt;/p&gt;

&lt;p&gt;Those things are just about all I need to work with TSV files comfortably.&lt;/p&gt;

&lt;h1&gt;Dealing with long lines&lt;/h1&gt;

&lt;p&gt;Sometimes I'll run some data through a statistics program and it'll tell me &quot;Invalid character at line 576, column 9438&quot;.  How do you jump to that location in your data?  Easily enough in normal mode:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;576G9438|
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;G&lt;/code&gt; jumps you to a line, &lt;code&gt;|&lt;/code&gt; jumps you to a column.  A good mnemonic to remember these: &quot;g = goto line&quot; and &quot;| looks like a column&quot;.&lt;/p&gt;

&lt;p&gt;Suppose (like me) you turn scrollbars off in your GVim window, or you work from a terminal.  How do you scroll horizontally?  With &lt;code&gt;zL&lt;/code&gt; and &lt;code&gt;zH&lt;/code&gt;.  But those are hard to type and harder to remember, so I map them to CTRL+Shift+Right and Left.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;nnoremap &amp;lt;C-S-Right&amp;gt; zL
nnoremap &amp;lt;C-S-Left&amp;gt; zH
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you have a lot of long lines, it behooves you to set up a mapping to turn soft line-wrapping on and off quickly:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;nnoremap &amp;lt;Leader&amp;gt;w :setlocal nowrap!&amp;lt;CR&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you can jump back and forth between wrapped and unwrapped views of your data via &lt;code&gt;\w&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;Diffs on long lines&lt;/h1&gt;

&lt;p&gt;When using vimdiff, &lt;code&gt;]c&lt;/code&gt; and &lt;code&gt;[c&lt;/code&gt; will jump you to lines that contain differences.  But if your lines are thousands of characters long, it doesn't always help to know that two lines are different: you want to know where they differ.  &lt;code&gt;]c&lt;/code&gt; puts you at the first column in the line, which isn't helpful.&lt;/p&gt;

&lt;p&gt;This function will jump you to the column where the difference between two lines starts:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function! IsDiff(col)
    let hlID = diff_hlID(&quot;.&quot;, a:col)
    return hlID == 24
endfunction

function! FindDiffOnLine()
    let c = 1
    while c &amp;lt; col(&quot;$&quot;)
        if IsDiff(c)
            call cursor(&quot;.&quot;, c)
            return
        endif
        let c += 1
    endwhile
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This seems fragile, so if you know a better way please leave a comment and let me know.&lt;/p&gt;

&lt;h1&gt;Diff a buffer against itself&lt;/h1&gt;

&lt;p&gt;Sometimes I have files with lots of lines of data, and some lines might be duplicate.  It's really easy to get rid of the duplicate lines:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:sort u
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But what if I want to see what lines were just removed?  I don't know of a built-in way to do it, so I use this simple function:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function! MarkDuplicateLines()
    let x = {}
    let count_dupes = 0
    for lnum in range(1, line('$'))
        let line = getline(lnum)
        if has_key(x, line)
            exe lnum . 'norm I *****'
            let count_dupes += 1
        else
            let x[line] = 1
        endif
    endfor
    echomsg count_dupes . &quot; dupe(s) found&quot;
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That'll put a bunch of asterisks at the beginning of every line that's a duplicate of a previous line.&lt;/p&gt;

&lt;h1&gt;Fix broken punctuation&lt;/h1&gt;

&lt;p&gt;Ever had to work with textual data that someone else sent you in MS Word or Excel?  I have.  It hurts.  If you copy/paste from an MS document into a plaintext file, you'll probably end up with a bunch of funky unreadable or undisplayable characters.&lt;/p&gt;

&lt;p&gt;This function fixes the most common Word-spawned &quot;smart&quot; punctuation characters that you're likely to run across:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function! FixInvisiblePunctuation()
    silent! %s/\%u2018/'/g
    silent! %s/\%u2019/'/g
    silent! %s/\%u2026/.../g
    silent! %s/\%uf0e0/-&amp;gt;/g
    silent! %s/\%u0092/'/g
    silent! %s/\%u2013/--/g
    silent! %s/\%u2014/--/g
    silent! %s/\%u201C/&quot;/g
    silent! %s/\%u201D/&quot;/g
    silent! %s/\%u0052\%u20ac\%u2122/'/g
    silent! %s/\%ua0/ /g
    retab
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This function was built up during years of choking on special characters that crept up in my data, so I'm sure I'll be adding more to it in the future.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\%u####&lt;/code&gt; here is a regex escape to represent a character as a hexidecimal Unicode codepoint.&lt;/p&gt;

&lt;p&gt;Many of these characters show up as blank (whitespace) if you lack the font to display them.  If you ever run across a character you can't see and you want to inspect it, put the cursor on it and do&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:ascii
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That's a bad name for the command, since it works on Unicode characters too.  That'll give you the numeric code you can use in a regex to replace them all with something sane.&lt;/p&gt;</description></item><item><title>Dell: the aftermath</title><link>http://briancarper.net/blog/551/dell-the-aftermath</link><guid>http://briancarper.net/blog/551/dell-the-aftermath</guid><pubDate>Thu, 08 Jul 2010 11:11:28 -0700</pubDate><description>&lt;p&gt;In a &lt;a href=&quot;http://briancarper.net/blog/547/dell-sucks&quot;&gt;previous post&lt;/a&gt; I outlined the ways in which Dell's customer service sucks.  I finally got my computer yesterday, a Studio XPS 9000.  Here are my first impressions.&lt;/p&gt;

&lt;!--more First impressions...--&gt;

&lt;h1&gt;The bad&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;This computer weighs so much I almost hurt my back lifting it.  I thought computers were supposed to be getting smaller and lighter?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The HD indicator light is tiny and on the top of the case.  I can't see it with my computer under my desk.&lt;/p&gt;

&lt;p&gt;The optical drive is behind one of those stupid plastic flap door things.  So there isn't even an indicator light for the DVD drive.  I'm seriously considering taking a screwdriver the case to fix this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It didn't come with a Windows install disk or a driver disk.  It only has a recovery partition on the HD.&lt;/p&gt;

&lt;p&gt;I found an &lt;a href=&quot;http://support.dell.com/support/topics/global.aspx/support/dellcare/en/backupcd_form&quot;&gt;order form&lt;/a&gt; which I think will get me my disks.  In the mail.  Seriously, Dell?  &lt;strong&gt;&lt;em&gt;Seriously?&lt;/em&gt;&lt;/strong&gt;  Why not come to my house and kick me in the balls while you're at it?&lt;/p&gt;

&lt;p&gt;The recovery partition doesn't help you worth a crap if you want to do things like repartition your drive to put Linux on it.  Windows7's sucky partition shrinking app wouldn't shrink it lower than 500GB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Dell recovery program is called DataSafe or something, and when you use it, it tries to upsell you like crazy to get a &quot;pro&quot; version that has a bunch of useless backup features.  Uggggghhhhh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The side of the case is white.  In 10 years it's either going to be yellow with age, or scuffed up beyond hope.  Kind of ugly, but I don't care much.  The front of the case looks OK though.  Black with red highlights.  About as good as I could expect.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It came pre-installed with some crappy &quot;Dell Dock&quot; knockoff of Apple's Dock.  Worthless and instantly uninstalled.  &lt;/p&gt;

&lt;p&gt;This thing caused the desktop icons to be hidden by default.  Who would possibly want to do this in Windows?  I can image everyone and their grandmother being awfully confused by the missing icons.&lt;/p&gt;

&lt;p&gt;When quitting this dock, it said &quot;Undo is not possible&quot;.  I love a program that has no going back once you quit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I wanted to find drivers for the wireless card that came with the Dell.  So I went to Dell's support site and typed in the tag number on my computer.  It gave me a link to drivers for the wrong card.  I had to google all over the place to find the right ones.  Way to go.&lt;/p&gt;

&lt;p&gt;Dell's website is a labyrinth full of outdated information and dead pages in general.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The instructions I got with the computer reference Vista.  I don't have Vista.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There's a &quot;Windows inside&quot; logo on the case.  It will be removed shortly.  They leave an awful lot of glue behind.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;The good?&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The i7 is about as fast as I had hoped.  It only took a half-dozen cores and 12 GB of RAM to let me watch full-screen flash videos on Youtube.  I feel so modern.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The inside of the case is OK.  There are a lot of hard drive bays and lots of extra screws.  It should be easy to expand if I need to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It came with bloatware and crapware, but actually far less than I was dreading.  And most of it was trying to sell you Dell crap.&lt;/p&gt;

&lt;p&gt;In the olden days you'd get a hundred links to AOL and other 3rd-party crap.  I saw a link to Skype and the obligatory nag to buy an anti-virus subscription (fat chance), but not much else.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dell delivered the computer 2 days past the original estimated delivery date.  So in spite of all the bullcrap and phone-jockeying I had to go through for billing, I can't complain about how fast it got here.  Two days late isn't bad.&lt;/p&gt;

&lt;p&gt;I've heard rumors that these computers are built in Malaysia, and mine was definitely shipped from the US (per the Purolator tracking site).  So I'm surprised they can get these things delivered as fast as they can given that it was shipped halfway around the world first, and had to go through Customs at least once coming from the US to Canada.&lt;/p&gt;

&lt;p&gt;Purolator was the only shipping option for Canada.  I would've preferred to rush it.  But maybe that's not possible given that it's coming from the US.  Oh well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It runs pretty quiet, given how huge the fans are.  We'll see how hot it gets once I start putting some load on it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It came with a DVI to VGA adapater and a DVI to HDMI adapter.  I thought that was a nice touch, though it could be that they come standard with any nVidia card nowadays.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Works OK with Linux.  It took 20 minutes to set up.  (Not counting wiping the Windows partition and re-installing on a smaller partition from my own copy, minus the crapware.  That took over an hour.)  Sound, video and wireless work out of the box in Linux.  All 12 GB of RAM are usable, given a 64 bit OS.  (I discovered this the fun way, by unthinkingly trying a 32 bit OS first.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It didn't burst into flames (yet).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It has a peanut tray on the top.  Or MP3-player tray, I guess.  But I really want to put peanuts in there.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;Brian, you're stupid!&lt;/h1&gt;

&lt;p&gt;So why did I get a Dell?  Because I had good experience with them in the past, at home and at work.  Given, that experience was 5 years in the past, and a lot can change.  And I'm new to Canada, and relatively unaware of what options exist here. &lt;/p&gt;

&lt;p&gt;The other (main) reason was that they were far, far cheaper than going through newegg.ca to get the same hardware.  But I guess you get what you pay for.  &lt;em&gt;Caveat emptor.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I wouldn't recommend Dell to anyone else, given how chaotic the whole buying process was.  Too much uncertainty, too much room for mistakes.&lt;/p&gt;

&lt;p&gt;Dave asked in my previous post why I didn't just a computer myself, like I had in the past.  I said I didn't have time, but what I meant wasn't build time, which should be an hour or two max.  I meant research time.  Trying to match up compatible hardware, trying to find the best prices on all the components, checking for Linux compatibility, this takes forever and a half.  I don't have hours / days to dork around with this any more.&lt;/p&gt;

&lt;p&gt;On the other hand I can just google &quot;xps 9000 linux&quot; and see instantly what problems people had.  I can be semi-confident that the hardware would all be compatible.  And that did work out OK.&lt;/p&gt;

&lt;p&gt;And the last reason I got Dell is that unfortunately I need Windows for work and gaming.  Blarg.  Paying the Windows tax to Dell is bad enough, let alone buying one off the shelf for $6,000 or however much they cost nowadays.&lt;/p&gt;</description></item><item><title>Sorry for the downtime</title><link>http://briancarper.net/blog/550/sorry-for-the-downtime</link><guid>http://briancarper.net/blog/550/sorry-for-the-downtime</guid><pubDate>Thu, 08 Jul 2010 08:29:37 -0700</pubDate><description>&lt;p&gt;This isn't the kind of email I like to see when I wake up:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Our backend monitoring system has detected an error on the host where your Linode resides which could lead to a failure condition. In order to protect your Linode, we have scheduled an emergency migration to a different host which will commence shortly. Please note that there is currently no issue with your Linode - this is a proactive measure we are taking to avoid an issue in the future.  &lt;/p&gt;
  
  &lt;p&gt;We apologize for any inconvenience this may cause. Should you have any questions or concerns, please do not hesitate to reply to this ticket.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The server rebooted and my sites didn't come back up, so there was some downtime.  Sorry about that.&lt;/p&gt;</description></item><item><title>In which border-radius is abused</title><link>http://briancarper.net/blog/548/in-which-border-radius-is-abused</link><guid>http://briancarper.net/blog/548/in-which-border-radius-is-abused</guid><pubDate>Mon, 05 Jul 2010 15:54:24 -0700</pubDate><description>&lt;p&gt;I threw together a new blog layout today.  I scaled back the level of cows a bit (just a bit, don't worry!) Criticism / feedback welcome.  (IE-related feedback should be dropped off in the circular file by my desk.)&lt;/p&gt;

&lt;p&gt;In what is surely a prelude to the future of the internets, I'm abusing &lt;code&gt;border-radius&lt;/code&gt; pretty heavily in this layout.  &lt;code&gt;border-radius&lt;/code&gt; is likely to become the new &lt;code&gt;marquee&lt;/code&gt; HTML tag or &lt;code&gt;text-shadow&lt;/code&gt; CSS attribute: Maybe an OK idea at first, but then everyone uses it so much it makes your eyes bleed.&lt;/p&gt;

&lt;p&gt;So I figured I'd best get my border-radiusing in early while it's still cool.  IE8 users, you still get pointy corners. Sucks to be you.&lt;/p&gt;

&lt;p&gt;Also, if you have any ideas for features I should implement for &lt;a href=&quot;http://github.com/briancarper/cow-blog&quot;&gt;cow-blog&lt;/a&gt;, please let me know.  I've been crawling the internet looking at blogs for ideas of things to implement and features to steal, but I'm running out of ideas.  It does everything I want now, but I'm not a reader.&lt;/p&gt;</description></item><item><title>Dell sucks</title><link>http://briancarper.net/blog/547/dell-sucks</link><guid>http://briancarper.net/blog/547/dell-sucks</guid><pubDate>Fri, 02 Jul 2010 10:57:41 -0700</pubDate><description>&lt;p&gt;Why did I order a computer from Dell?  I guess I had a good opinion from 6 years ago when I last bought something from them.  &lt;/p&gt;

&lt;p&gt;Let's count the ways in which their customer service has failed me.  (And my computer isn't even here yet.)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://briancarper.net/blog/546/i-am-an-edge-case&quot;&gt;As documented&lt;/a&gt;, their website couldn't process my credit card without a phone call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After a week of my computer being &quot;in production&quot;, I started getting more phone calls from an unidentified phone number that Google told me was Dell.  Fearing another billing problem, I called back.  And I was told &quot;Thanks for calling, but our order tracking system is down.  And we're all going home.  Call back tomorrow morning.&quot;.&lt;/p&gt;

&lt;p&gt;If only Dell had some means to acquire reliable computer systems on which to build their order tracking database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I called the next day and was told my order was fine.  I was also told (per script, I'm certain) that I could check my order status on Dell's website.  Which of course I knew.  I know it costs the company money every time someone calls, and they try to strongly discourage calls for that reason, but their script made it sound like I was an imbecile.  &lt;/p&gt;

&lt;p&gt;I found it quite condescending.  I dislike these canned scripts pander to the lowest common denominator of customer.  They should be happy to take my call.  I just spend upwards of a thousand dollars on their crap.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turns out the phone calls I was getting were from someone trying to give me &quot;&lt;em&gt;free internet from Shaw or Telus for 3 months&lt;/em&gt;&quot;, and I was eligible because I bought a Dell computer.  So I was being telemarketed before my computer even got here.&lt;/p&gt;

&lt;p&gt;I said I already had internet service, and they said &quot;Oh, too bad, it's for new customers only.&quot;  I do not appreciate this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I got an email saying my order shipped.  Joy!  20 minutes later I got an email saying my order was delayed, and if it didn't ship in 5 days I should call.  What?&lt;/p&gt;

&lt;p&gt;It really did ship though, I have a tracking number.  Why the contradictory emails?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of my phone dealings with Dell were via some offshored far-eastern country, judging by the accents of the phone reps.  I have nothing against this in principle; I'm not a xenophobe.  But the phone connection is always so static-filled and laggy that it really puts a damper on communication.&lt;/p&gt;

&lt;p&gt;My computer isn't here yet, and I just hope to God it works and doesn't break in a month.  I kind of wish &lt;a href=&quot;http://hardware.slashdot.org/article.pl?sid=10/07/01/2321230&quot;&gt;this article&lt;/a&gt; had come out a week earlier.  &lt;/p&gt;

&lt;p&gt;That'll teach me for trying to save time, I guess.  Next time I'll build my own system from scratch.  Dell goes onto my List of Companies Not to Buy From in the Future (LCNBFF), along with &lt;a href=&quot;http://briancarper.net/tag/12/westinghouse&quot;&gt;Westinghouse&lt;/a&gt; and oh so many others.&lt;/p&gt;</description></item><item><title>I am an edge case</title><link>http://briancarper.net/blog/546/i-am-an-edge-case</link><guid>http://briancarper.net/blog/546/i-am-an-edge-case</guid><pubDate>Wed, 30 Jun 2010 10:03:20 -0700</pubDate><description>&lt;p&gt;I am an alien.  An American who emigrated to Canada.  This has resulted in a lot of fun and a bit of pain as I've managed to break the systems of many of the businesses I deal with.&lt;/p&gt;

&lt;p&gt;As a programmer I can appreciate the importance (and sometimes difficulty) of handling edge cases.  It's been an interesting experience living as an edge case myself.&lt;/p&gt;

&lt;!--more And hilatiry ensued--&gt;

&lt;h1&gt;H&amp;amp;R Block(heads)&lt;/h1&gt;

&lt;p&gt;Taxes are confusing enough when you don't have a wife from another country.  The friendly folk at H&amp;amp;R Block had no idea how to handle my situation.  Their computers demanded a Social Security Number for my wife, which she doesn't have, because she's Canadian.  So they left it blank.  (Leaving it blank was the consensus opinion of everyone at H&amp;amp;R Block, including the managers.)&lt;/p&gt;

&lt;p&gt;To even be able to leave it blank, they had to print the forms, because the computer refused to submit my taxes electronically with a blank SSN.  This should've been a red flag to me, looking back.&lt;/p&gt;

&lt;p&gt;The correct thing to do was for her to get an ITIN from the US, which from what I know is a like an SSN that doesn't get you SS benefits or allow you to work in the US.  It's just for tracking.  But they didn't tell me that.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Moral of this story: That's the last time I'll ever use H&amp;amp;R Block.  If I want to do my taxes wrong, I can do that myself for free.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;IRS&lt;/h1&gt;

&lt;p&gt;The IRS refused to believe that my wife existed without a number to assign her, so they rejected my tax return and threatened to charge me tons of penalties.  So I drove down to the friendly neighborhood IRS office.  Surely they'd know how to fix this, right?&lt;/p&gt;

&lt;p&gt;Wrong.  The fellow at the IRS was familiar with filing taxes for Mexicans living in the US, but not for a &quot;non-resident alien Canadian spouse&quot; like mine.&lt;/p&gt;

&lt;p&gt;The ITIN docs say that I need to submit a notarized copy of my wife's passport, to get her an ITIN.  But notarized by whom?  By a notary in Canada or the US?  The IRS agent spent at least an hour reading his enormous IRS manual, looking up treaties and international law, trying to figure this out.  Eventually he found a footnote scribbled into the margin of his book that said a Canadian notary was OK.  So that's what I gave him.&lt;/p&gt;

&lt;p&gt;He filled out the new tax forms himself, stapled on a photocopy of the page from his own IRS manual saying the Canada-notarized passport was OK, stamped everything all official-like, mailed it away himself, and... a month later it was rejected again.  I needed to get a US notary to do it, or my wife had to drive to the freaking capital of her province to get it super-notarized or something.  Nine months later and 3 more trips to the IRS office, I finally got it worked out.&lt;/p&gt;

&lt;p&gt;As a US citizen, living in Canada, working for a US company, being paid by a Canadian payroll company, I live in constant fear of doing my taxes next spring.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Moral of this story: Even the IRS doesn't know their own tax code.  Thanks again, US Government.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;No credit&lt;/h1&gt;

&lt;p&gt;I have good credit in the US.  I was able to get a car loan without a co-signer 6 years ago.  I have credit cards.&lt;/p&gt;

&lt;p&gt;In Canada, I don't exist.  My credit score here is zero, as you might expect.  I tried to buy a car, and they just flat-out wouldn't let me without a co-signer for a loan.  I guess I can't blame them.  (Except that I have money, they have something I want to buy, and we both ended up losing out.)&lt;/p&gt;

&lt;p&gt;I went to the bank to open a checking (er, &quot;chequing&quot;) account, and all hell broke loose.  They wanted my business, but how could they justify giving an account to someone with &quot;no credit&quot;?  Eventually the bank manager managed to run a US credit check on me (at least she said she did) and they let me open an account.&lt;/p&gt;

&lt;p&gt;But I can't get a credit card here.  Not even a $500-limit high-interest card like they give to kids in college.  Not even after I had the bank write a letter of recommendation vouching for me.  I have to wait a year and keep getting paychecks before I show up in the system.  (&quot;Paycheques&quot;?)  I'm still looking for other options in the meantime.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Moral of this story: Well, no moral.  Sucks to be me, I guess.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;Shopping&lt;/h1&gt;

&lt;p&gt;I tried to buy a computer online recently (from Dell, which I'm starting to regret).  Not having a Canadian credit card, I used my US card.  This is OK, my card works up here, with a small foreign transaction fee of %1-3.&lt;/p&gt;

&lt;p&gt;But the website wouldn't take a US address as my billing address.  I had to give a Canadian address.  &quot;Province&quot; was a drop-down, mandatory field.&lt;/p&gt;

&lt;p&gt;No matter, I'll just go to my bank's website and change my billing address to one in Canada.  I checked with my bank before I moved, and they said it was no problem to keep the account even if I moved to another country.  &quot;&lt;em&gt;We have lots of international customers!&lt;/em&gt;&quot;, said the teller.&lt;/p&gt;

&lt;p&gt;Well, my bank's website won't let me specify a non-US billing address.  &quot;State&quot; is a mandatory drop-down.  Which is awesome.  I emailed the bank and asked them to change my address for me, and they did.  Now when I go to the &quot;change my address&quot; form on their website, half the fields are filled in and half are just blank.  So if I ever use the form again, something will probably break.&lt;/p&gt;

&lt;p&gt;Once I got on the phone with Dell, they were more than happy to take my US credit card as payment.  Their online form just couldn't handle it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Moral of this story: Text fields, not drop-downs.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;An so on&lt;/h1&gt;

&lt;p&gt;When I fly across the border, I have to fill out a customs declaration form.  There's a field asking &quot;country of residence&quot;.  Well, technically I am a resident of two countries.  So I pick &quot;US&quot; when I fly down to the States, &quot;Canada&quot; when I fly back.&lt;/p&gt;

&lt;p&gt;I tried to get a Costco membership in Canada, and they wanted a driver license.  My license is from Oregon.  Hilarity ensued, and they decided I didn't need one after all.  Any time I need to give a driver license for any purpose, I end up breaking something.&lt;/p&gt;

&lt;p&gt;Note that in every situation I've described, what I was trying to do was valid, and after some hassle, everything usually worked out OK.  Computers just got in the way and slowed the process way down.  And I'm not &lt;em&gt;that&lt;/em&gt; much of an edge case.  250,000 people immigrate to Canada every year.&lt;/p&gt;

&lt;p&gt;I suppose it might be better to optimize for the common cases, force people to pick their province from a drop-down.  And then deal with the edge cases like mine manually later.  Every text field is another opportunity for users to type in gibberish and chaos.  But I wonder if the programmers actually thought about it this much, or if they were just being lazy.&lt;/p&gt;

&lt;p&gt;I'm not really complaining.  I don't expect the world to change to accommodate me.  It's been more funny than annoying.  But I do find it interesting to see the flaws in computer systems exposed.  I get a certain sick satisfaction out of seeing people write &quot;invalid&quot; values into fields that I know are going to break someone's database down the line.&lt;/p&gt;</description></item><item><title>Goodbye Tokyo Cabinet, hello PostgreSQL?</title><link>http://briancarper.net/blog/545/goodbye-tokyo-cabinet-hello-postgresql</link><guid>http://briancarper.net/blog/545/goodbye-tokyo-cabinet-hello-postgresql</guid><pubDate>Tue, 29 Jun 2010 11:32:58 -0700</pubDate><description>&lt;p&gt;The first version of this blog used MySQL; then I switched to Tokyo Cabinet.  But now I've switched back to PostgreSQL.  Here's why.&lt;/p&gt;

&lt;!--more Read on.--&gt;

&lt;h1&gt;Why did I switch to TC to begin with?&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;There weren't any good ORM-type libraries for Clojure at the time (over a year ago).  So there was a bit of an impedance mismatch trying to query and work with my data.  In the DB I have separate tables for posts, comments, tags, categories.  But 90% of the time I want to fetch a post and end up in Clojure with all related tags, comments etc.  (A JOIN won't work here without a lot of work to un-mangle the results; you usually need multiple queries.)  &lt;/p&gt;

&lt;p&gt;With TC I could store anything in the DB, so I just dumped a post into the DB as serialized hash-maps with all the comments, tags, and categories as sub-keys.  So querying was easy.  (Or was it?  More below.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MySQL was really slow. This is largely because my queries were terrible, as I tried to solve the problem from #1 via brute force.  TC on the other hand is fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I tried to solve #2 by using a Clojure ref as a cache.  But tying the STM to a database's transaction system is as far as I know difficult or impossible right now (per many threads on the mailing list).  I had a lot of potential race conditions, which (as far as I know) never bit me, but probably would've eventually.  I had to deal with keeping the cache up-to-date as comments were posted and posts were added and deleted and renamed.  Remember:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&quot;There are only two hard problems in Computer Science: cache invalidation and naming things.&quot; --Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;So why did I stop using TC?&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I have no idea how to use a key/value store database properly.  TC will take anything you dump into it, which is both a strength and a weakness.&lt;/p&gt;

&lt;p&gt;There's a lot of crap you have to do by hand that a proper database does for you.  Consider checking for null values, for example; I ended up with a lot of &lt;code&gt;nil&lt;/code&gt;s in my data because my validations weren't 100% foolproof, or because I imported data via code that didn't run the validations and I never noticed..  Or enforcing uniqueness of values; I had tag objects in the database with the same key but different values (due to capitalization differences), which screwed up a lot of stuff.&lt;/p&gt;

&lt;p&gt;On the other hand, there's a lot of information about how to use RDBMS properly, and I have a lot of experience with it already.  Constraints are easy to set up.  Columns have types, which is nice.  (Strange that I gravitate toward statically-type databases while I gravitate toward dynamically-typed programming languages.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I have to compile and install Tokyo Cabinet by hand on my Linux distro.  It's probably not worth distro maintainers to maintain a package that so few people use.  MySQL and PostgreSQL have lots of people working on keeping them running OK on most Linux distros.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some kinds of queries were still awkward in TC.  &quot;Give me post X&quot; was great: I'd also get all the tags, categories etc. for free.  But then how do you query to get all tags across all posts?  Or all comments?  Fetch all the posts and iterate over them, collecting their tags, then uniquify the resulting list?  Not so pretty, and not so fast.  So I was back to caching again, which still gave me nightmares about race conditions and dirty data.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;So now why am I using an RDBMS again?&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;An RDBMS is exactly what I really need, if I could just query the thing concisely and get it to run fast.  Thankfully there are some ORM-like libraries for Clojure in the works nowadays, already usable for a hobby project like this blog.  There are &lt;a href=&quot;http://github.com/duelinmarkers/clj-record&quot;&gt;clj-record&lt;/a&gt;, &lt;a href=&quot;http://github.com/brentonashworth/carte&quot;&gt;Carte&lt;/a&gt;, &lt;a href=&quot;http://gitorious.org/clojureql/&quot;&gt;ClojureQL&lt;/a&gt;, and my own &lt;a href=&quot;http://github.com/briancarper/oyako&quot;&gt;Oyako&lt;/a&gt;, and possibly others in the works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For my tiny blog's database, Oyako gives me slightly slower performance than TC, but along the same order of magnitude, which is good enough.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Via Oyako I can (fairly concisely) fetch posts and get the associated tags, comments etc.  But I can also easily fetch all tags, or all comments, since they're in their own tables.  The &quot;relational&quot; part of RDBMS does come in handy sometimes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;Summary version&lt;/h1&gt;

&lt;p&gt;I switched to TC to begin with because I was using SQL wrong, and it was too slow and clumsy.  Once I figured out how to use SQL correctly, it was a no-brainer to go back.&lt;/p&gt;</description></item><item><title>Introducing Gaka</title><link>http://briancarper.net/blog/543/introducing-gaka</link><guid>http://briancarper.net/blog/543/introducing-gaka</guid><pubDate>Mon, 28 Jun 2010 17:59:26 -0700</pubDate><description>&lt;p&gt;The CSS for &lt;a href=&quot;http://github.com/briancarper/cow-blog&quot;&gt;my blog&lt;/a&gt; is now being generated via &lt;a href=&quot;http://github.com/briancarper/gaka&quot;&gt;gaka&lt;/a&gt;, a CSS-generating library I wrote this afternoon.  It's extremely simple, but it got the job done for me.  I turned around 600 lines of CSS into around 250 lines of Clojure without much effort.  It looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;user&amp;gt; (require '(gaka [core :as gaka]))
nil
user&amp;gt; (def rules [:div#foo
                  :margin &quot;0px&quot;
                  [:span.bar
                   :color &quot;black&quot;
                   :font-weight &quot;bold&quot;
                   [:a:hover
                    :text-decoration &quot;none&quot;]]])
#'user/rules
user&amp;gt; (println (gaka/css rules))
div#foo {
  margin: 0px;}

  div#foo span.bar {
    color: black;
    font-weight: bold;}

    div#foo span.bar a:hover {
      text-decoration: none;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Gaka is partly inspired by &lt;a href=&quot;http://sass-lang.com/&quot;&gt;Sass&lt;/a&gt;, which I found very pleasant to work with recently.  And it's partly inspired by &lt;a href=&quot;http://github.com/weavejester/hiccup&quot;&gt;Hiccup&lt;/a&gt;, which is a delicious way to generate HTML in Clojure.  &lt;/p&gt;

&lt;p&gt;There's more info and more examples on &lt;a href=&quot;http://github.com/briancarper/gaka&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>New blog engine up and running</title><link>http://briancarper.net/blog/542/new-blog-engine-up-and-running</link><guid>http://briancarper.net/blog/542/new-blog-engine-up-and-running</guid><pubDate>Wed, 23 Jun 2010 15:48:24 -0700</pubDate><description>&lt;p&gt;Well, my new blog is up and running.  Sorry for the temporary lack of cows in my layout.  I'm dogfood-testing the blog engine in a fairly vanilla state until I work out some of the bugs.  This layout is based upon &lt;a href=&quot;http://shaheeilyas.com/archives/barecity/&quot;&gt;barecity&lt;/a&gt;, a minimalist Wordpress theme that I adapted easily enough to my blog.&lt;/p&gt;

&lt;p&gt;As a bonus, I applied a dirty hack to my RSS feed that I think should help avoid screwing up people's RSS readers with duplicate entries.&lt;/p&gt;

&lt;p&gt;I'll write again soon with some info about the blog engine and some things I learned writing it.&lt;/p&gt;

&lt;p&gt;(As mentioned previously, &lt;a href=&quot;http://github.com/briancarper/cow-blog/tree/0.2.0&quot;&gt;here's the code&lt;/a&gt;.)&lt;/p&gt;</description></item><item><title>Breaking links is easy to do</title><link>http://briancarper.net/blog/breaking-links-is-easy-to-do</link><guid>http://briancarper.net/blog/breaking-links-is-easy-to-do</guid><pubDate>Tue, 22 Jun 2010 23:23:27 -0700</pubDate><description>&lt;p&gt;I apologize in advance to everyone who subscribes to my blog's RSS feed, but this week your RSS reader is probably going to suddenly find 25 &quot;new&quot; posts from me.&lt;/p&gt;

&lt;p&gt;My blog currently uses &lt;code&gt;/blog/title&lt;/code&gt; as the URL scheme, with similar URLs for categories and tags etc.  Soon, I'm probably going to change it to &lt;code&gt;/blog/123/title&lt;/code&gt;, as part of the impending release of version 0.2 of my blog engine.  (The code-in-progress is in a &lt;a href=&quot;http://github.com/briancarper/cow-blog/tree/0.2.0&quot;&gt;branch on github&lt;/a&gt;, for the daring and foolish among you.)&lt;/p&gt;

&lt;p&gt;This way, I can change the title of a post without breaking everything.  I have heretofore lacked this ability.  It's easy to code, you just tell Compojure to ignore everything after the number in a route.  Something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defroutes foo
  (GET [&quot;/blog/:id:etc&quot; :id #&quot;\d+&quot; :etc #&quot;(/[^/]*)?&quot;] [id]
    (pages/post-page id)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's only a few lines of code to change, but the ramifications are widespread.  It'll instantly break every link to my blog, for example.  At least it's pretty easy to set up a bunch of redirects in Compojure to avoid that.  I think this'll work:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(require (blog [db :as db]
               [link :as link])
         (oyako [core :as oyako])
         (ring.util [response :as response]))

(defn redirect-post [name]
  (when-let [post (oyako/fetch-one db/posts :url name)]
    (response/redirect (link/url post))))

(defroutes redirect-routes
  (GET [&quot;/blog/:name&quot; :name #&quot;[^/]+$&quot;] [name]
    (redirect-post name)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(&lt;a href=&quot;http://github.com/briancarper/oyako&quot;&gt;Oyako&lt;/a&gt; here is the experimental ORM-like library I'm using to interface with PostgreSQL nowadays, having ditched Tokyo Cabinet.)&lt;/p&gt;

&lt;p&gt;Changing my URL scheme is also going to mess up RSS though, because I (foolishly) used post URLs as the GUIDs in my RSS feed up to this point.  This problem I don't know how to avoid.  I might reduce the number of posts included in my feed temporarily, to limit the damage.&lt;/p&gt;</description></item><item><title>Vim regex - remove kind-of-matching lines</title><link>http://briancarper.net/blog/vim-regex---remove-kind-of-matching-lines</link><guid>http://briancarper.net/blog/vim-regex---remove-kind-of-matching-lines</guid><pubDate>Sun, 20 Jun 2010 22:49:19 -0700</pubDate><description>&lt;p&gt;I have a file where every line starts with a number (followed by whitespace and a bunch of other stuff).  Every number appears on either one or two lines, and if two, the second line always has a &lt;code&gt;b&lt;/code&gt; after the number.&lt;/p&gt;

&lt;p&gt;I need to delete every line for which there's a corresponding &lt;code&gt;b&lt;/code&gt; line.  But if there's no corresponding &lt;code&gt;b&lt;/code&gt; line I want to leave the original line there.&lt;/p&gt;

&lt;p&gt;Before:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;123 foo bar
456 blarg
789 quux
123b foo baz
789b quux blurble
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;123b foo baz
456 blarg
789b quux blurble
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Except in my real file, I have a thousand lines and it'd take a year to do by hand.  Vim to the rescue:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:sort
:%s/^\v((\d+).*\n)(\2b.*)/\3/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that is why Vim is awesome.  Can you think of a shorter way to do this, in Vim or Emacs?&lt;/p&gt;</description></item><item><title>Ads on license plates?</title><link>http://briancarper.net/blog/ads-on-license-plates</link><guid>http://briancarper.net/blog/ads-on-license-plates</guid><pubDate>Sun, 20 Jun 2010 22:09:08 -0700</pubDate><description>&lt;p&gt;What if when your car stops at a red light, your &lt;a href=&quot;http://www.mercurynews.com/ci_15338527?IADID=Search-www.mercurynews.com-www.mercurynews.com&amp;amp;IADID=Search-www.mercurynews.com-www.mercurynews.com&amp;amp;nclick_check=1&quot;&gt;license plate displays ad banners&lt;/a&gt;?  What could possibly go wrong?&lt;/p&gt;

&lt;p&gt;Quoth the person(?) who wrote this bill:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&quot;We're just trying to find creative ways of generating additional revenues,&quot; he said. &quot;It's an exciting marriage of technology with need, and an opportunity to keep California in the forefront.&quot; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The forefront of annoying the hell out of people.  Certainly what I need is more distractions on the road.  I mean, what if there's a new brand of toothpaste and I didn't find out yet?  Someone somewhere needs to earn a dime for telling me about it by any means necessary.&lt;/p&gt;

&lt;p&gt;I'm just waiting for the first company to propose paying new parents a few hundred dollars to tattoo ads on their babies.&lt;/p&gt;</description></item><item><title>Printer spam: what could possibly go wrong?</title><link>http://briancarper.net/blog/printer-spam-what-could-possibly-go-wrong</link><guid>http://briancarper.net/blog/printer-spam-what-could-possibly-go-wrong</guid><pubDate>Thu, 17 Jun 2010 10:59:57 -0700</pubDate><description>&lt;p&gt;As further evidence that there are no depths to which companies won't stoop when it comes to advertising, HP has come up with a great idea: Get people to hook their printers up to the internet and then &lt;a href=&quot;http://www.computerworld.com/s/article/9178128/HP_partners_with_Yahoo_for_targeted_ads&quot;&gt;spew advertisements out of their printers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, it's a win-win situation for the companies doing the advertising: Not only will people see your ads, they'll pay for the ink and paper to print them.  Maybe not such a great situation for the end-user though.&lt;/p&gt;

&lt;p&gt;And then there are the privacy implications of targeting ads based on geolocating the IP address of the printer.  Which I find a bit disturbing, but I guess advertisers already do that with online ads.  But wait, there's more:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ads can also be targeted based on a user's behavior as well as the content, said Vyomesh Joshi, head of the HP's Imaging and Printing Group.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Looking at what I'm printing so you can try to sell me things?  Just a bit creepy.&lt;/p&gt;

&lt;p&gt;Most troubling to me is the intrusiveness of the whole thing.  They're taking control of a physical object in my house and using it against me.  May as well kidnap my cat and train him to spell out &quot;BUY PEPSI&quot; in his cat litter.&lt;/p&gt;

&lt;p&gt;Quote some slimeball at HP:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&quot;What we discovered is that people were not bothered by it [an advertisement],&quot; Nigro said. &quot;Part of it I think our belief is you're used to it. You're used to seeing things with ads.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Translation: &quot;&lt;em&gt;We know this is a really horrible idea, but if people are complacent enough to sit there and take it without complaint, what's stopping us?&lt;/em&gt;&quot;&lt;/p&gt;

&lt;p&gt;He's right though, people are used to it.&lt;/p&gt;

&lt;p&gt;I guess TV, radio, internet, phones, product placement in movies and games, print media, billboards and the postal service just aren't enough.  Clearly what the world really needs is another ad-delivery mechanism.&lt;/p&gt;</description></item><item><title>Emacs creating zombie buffers</title><link>http://briancarper.net/blog/emacs-creating-zombie-buffers</link><guid>http://briancarper.net/blog/emacs-creating-zombie-buffers</guid><pubDate>Mon, 14 Jun 2010 15:44:18 -0700</pubDate><description>&lt;p&gt;Note to self:&lt;/p&gt;

&lt;p&gt;Using bleeding-edge swank-clojure and Slime, Emacs was creating buffers that didn't point to any file, every time &lt;code&gt;C-c C-k&lt;/code&gt; wanted to display warnings.  This was really quite annoying as it resulted in a mess of empty buffers being created.  This fixed it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(setq slime-highlight-compiler-notes nil)
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Clojure, from a Ruby perspective</title><link>http://briancarper.net/blog/clojure-from-a-ruby-perspective</link><guid>http://briancarper.net/blog/clojure-from-a-ruby-perspective</guid><pubDate>Wed, 09 Jun 2010 13:22:58 -0700</pubDate><description>&lt;p&gt;Fogus' recent article &quot;&lt;a href=&quot;http://blog.fogus.me/2010/06/09/clojure-rb/&quot;&gt;clojure.rb&lt;/a&gt;&quot; speculates about why there seem to be so many Ruby users adopting Clojure.  As a Ruby user who adopted Clojure, I figured I'd write about my experiences.&lt;/p&gt;

&lt;p&gt;What do Ruby and Clojure have in common, that would attract a Rubyist to Clojure?  A lot.  Obviously, this is somewhat subjective and I don't expect anyone else to agree, but this is what did it for me.&lt;/p&gt;

&lt;!--more Ruby and Clojure comparison ahead--&gt;

&lt;h1&gt;Semantic consistency&lt;/h1&gt;

&lt;p&gt;In Ruby, everything is an object.  It makes it simple to write code without worrying much about what kind of thing you have.  &lt;code&gt;foo.some_method(1,2,3)&lt;/code&gt; will generally work for any foo.&lt;/p&gt;

&lt;p&gt;In Clojure, everything is &lt;em&gt;not&lt;/em&gt; an object, specifically because it inherits primitives from Java land (though this doesn't hurt much in the kind of everyday use I put Clojure to).  But also because Clojure by design doesn't even attempt to be object-oriented.&lt;/p&gt;

&lt;p&gt;Clojure does have abstractions though.  For example there's an abstraction that says &quot;this thing can be called like a function&quot;.  And then you can treat any callable-thing as a function without worrying about what it is.&lt;/p&gt;

&lt;p&gt;In Ruby, a lot of things are Enumerable, which means you can do &lt;code&gt;foo.each{}&lt;/code&gt; and other similar things for a lot of different types of foo.  Clojure has something similar with its &lt;code&gt;seq&lt;/code&gt; abstraction.  Similarly, most Clojure data types and many Java ones are &lt;code&gt;seq&lt;/code&gt;-able, and most built-in core functions can iterate over the guts of various things using &lt;code&gt;seq&lt;/code&gt;s.  This includes regex matches, strings, directories of files, and so on.&lt;/p&gt;

&lt;p&gt;Ruby-style OOP brings a lot of complexity and baggage which Clojure avoids by not being OOP.  For example (ignoring Java for the moment), in Clojure land you don't have to worry about a member being public/private/protected, and there are few times when you have to worry about inheritance and class hierarchies.  (And there's the whole thread-safety thing.)  In Clojure data is stupid and immutable, and functions are just things that take input and give output, usually side-effect free.  The separation is clean and this results in programs that are very easy to reason about.&lt;/p&gt;

&lt;p&gt;Another example of consistency: Expressions.  In both Ruby and Clojure, everything has a value.  Things that are &quot;statements&quot; in other languages are instead expressions that return something.  This alone makes a lot of programs just a little bit better/easier to write.&lt;/p&gt;

&lt;h1&gt;Aesthetics&lt;/h1&gt;

&lt;p&gt;Like Ruby, Clojure code tends to be terse and expressive.&lt;/p&gt;

&lt;p&gt;Ruby reads like poetry, because it's mostly words and not so much punctuation.  In Ruby, you have none of Perl's sigils, very little of C's semi-colon line-endings and curly-delimited blocks.  Especially when you start omitting optional parentheses, it has a very minimalistic vibe which is appealing to many.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    def foo(bar)
      bar.each do |x|
        puts x
      end
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Clojure's s-expressions are another story, of course.  Some love them, some hate them.  Personally, I love them.  The tired old trope about Lispers not paying attention to parentheses is true; after a while they blend into the background.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    (defn foo [bar]
      (doseq [x bar]
        (prn x)))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What I see:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    defn foo [bar]
      doseq [x bar]
        prn x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Clojure has the same minimalistic feel to it, in my eyes.  It also helps that so many Clojure function names are short and concise.  And being able to use punctuation like &lt;code&gt;?&lt;/code&gt; and &lt;code&gt;!&lt;/code&gt; in variable/function names (&lt;code&gt;true?&lt;/code&gt; and &lt;code&gt;false?&lt;/code&gt; are function names), hyphens instead of underscores... these things help Clojure read smoothly.&lt;/p&gt;

&lt;p&gt;But along with aesthetics, in Clojure you get the benefits of s-exps: no order of operations to deal with, and absolute consistency.  Everything is &lt;code&gt;(function param1 param2 param3)&lt;/code&gt;.  Look at how many syntax rules you have to memorize for the Ruby code above to make sense.  Dot means method call, blocks are &lt;code&gt;do&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt; delimited and have those weird pipes in there, etc. &lt;/p&gt;

&lt;h1&gt;Literals and syntax sugar&lt;/h1&gt;

&lt;p&gt;Ruby has literal syntax for many types.  This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;:symbols&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[arrays]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{:hash =&amp;gt; :maps}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/regex/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;do block end&lt;/code&gt; and &lt;code&gt;{block}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This really is a big deal.  I'm spoiled and I can't use a language without these things nowadays.  It saves a ton of typing and it makes those things stand out in the code, making it both easier to write and to read.  I use those structures all the time in every program I write, so they should have a terse representation.&lt;/p&gt;

&lt;p&gt;Clojure has literal support for the same types as Ruby, and they even look mostly the same (with &lt;code&gt;#&quot;regex&quot;&lt;/code&gt; being a change I can live with).  And then it also has (among others):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#{sets}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#(function-literals %)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'(quoted forms)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;`(quasi-quoted ~forms)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is this a contradiction of my last point?  What happened to s-expressions and consistency?  Well, in Clojure, the reader shortcuts are just sugar that reduce to s-exps.  You can avoid all use of that sugar if you hate it.  &lt;code&gt;(hash-map :key &quot;val&quot;)&lt;/code&gt;, &lt;code&gt;(vector 1 2 3)&lt;/code&gt;, &lt;code&gt;(quote foo)&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;But more importantly, let's draw a(n arbitrary) distinction between &quot;good&quot; syntax and &quot;bad&quot; syntax.  &lt;/p&gt;

&lt;p&gt;Clojure's reader-macro sugar makes your code shorter, but doesn't change the structure of your code.  Take a function call &lt;code&gt;(f x y z)&lt;/code&gt;, and you can always substitute a vector or regex literal or quoted form into it.  &lt;code&gt;(f [vector] #{set} #&quot;regex&quot;)&lt;/code&gt;.  The syntax sugar is very local, very self-contained.  It doesn't leak into the surrounding code.  And of course you can combine them in nearly arbitrary ways:  &lt;code&gt;'[quoted vector]&lt;/code&gt;, &lt;code&gt;{:hash-map-containing-a #{:set 'of #(functions)}}&lt;/code&gt;.  This is &quot;good&quot; syntax.&lt;/p&gt;

&lt;p&gt;Compare this to things like the &lt;code&gt;x ? y : z&lt;/code&gt; construct, or heredocs.  These things are not as orthogonal.  They not only mean something on the &quot;inside&quot;, they also influence and interact with the code before and after them, thanks to precedence rules and special parsing rules.  Can you stick a heredoc in the middle of a function call?  Maybe (I don't even know), but have fun with the indentation and line-breaks if so.  When should you use &lt;code&gt;do&lt;/code&gt;/&lt;code&gt;end&lt;/code&gt; and when should you use &lt;code&gt;{}&lt;/code&gt; for blocks?  When do you need parens around your ternary if-then-else construct and when don't you?  When do you need to use &lt;code&gt;and&lt;/code&gt; and when &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;?  That's the &quot;bad&quot; kind of syntax.  Sometimes, maybe even most of the time, it makes your code shorter, but there are a lot of rules to memorize and you never know when you'll be bitten.&lt;/p&gt;

&lt;p&gt;Clojure largely avoids the &quot;bad&quot; syntax while taking advantage of the &quot;good&quot;.  Reader macros make your code shorter and visually easier to scan, but they rarely require you to do backflips to get your code to compile or run properly.&lt;/p&gt;

&lt;h1&gt;First-class functions&lt;/h1&gt;

&lt;p&gt;Ruby's blocks and &lt;code&gt;yield&lt;/code&gt; and friends let you deal with first-class functions.  This is a huge step in the Lisp direction already, and it's one of the things that makes Ruby great.  But there are limits.  Blocks use funky, special syntax, and in idiomatic Ruby, you will pass around only one block per method.  There's the whole &lt;code&gt;lambda&lt;/code&gt; and &lt;code&gt;proc&lt;/code&gt; mess, and then there are methods-as-objects which are different still.  And a lot of Ruby just calls &lt;code&gt;.send&lt;/code&gt; on an object and passes in a method name as a symbol.&lt;/p&gt;

&lt;p&gt;Being a Lisp, Clojure takes this a bit further.  First-class functions are ingrained in nearly everything you do in Clojure.  And they are easy to define and easy to call.  Define &lt;code&gt;f&lt;/code&gt; via &lt;code&gt;defn&lt;/code&gt; (to make it top-level), &lt;code&gt;fn&lt;/code&gt; (for a local function), or &lt;code&gt;#()&lt;/code&gt; (sugar for &lt;code&gt;fn&lt;/code&gt;), and then call it like &lt;code&gt;(f)&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Clojure also takes advantage of some functional-programming mainstays like &lt;code&gt;partial&lt;/code&gt; and &lt;code&gt;complement&lt;/code&gt; and &lt;code&gt;comp&lt;/code&gt;(osition).  We're not in full-blown Haskell territory, but it's a lot more FP than idiomatic Ruby.&lt;/p&gt;

&lt;p&gt;And hash-maps, vectors, sets, keywords, and symbols are also callable as functions in Clojure.  &lt;code&gt;({:foo 1} :foo)&lt;/code&gt; =&gt; &lt;code&gt;1&lt;/code&gt;.  Many things can be treated as functions.&lt;/p&gt;

&lt;h1&gt;Metaprogramming&lt;/h1&gt;

&lt;p&gt;In Ruby you can mess with the innards of any class you want.  There are facilities for defining methods dynamically, opening and inspecting classes at runtime, catch-all handlers for undefined methods, and all kinds of other dark magic.  But again there are limits... Ruby needs to make use of &lt;code&gt;eval&lt;/code&gt; to get certain things done.  And monkey-patching is a shotgun aimed at your foot.&lt;/p&gt;

&lt;p&gt;Well, if you like metaprogramming, Lisp macros are top of the line.  You can abstract away boilerplate with a vengeance.  Macros are the ultimate application of DRY.&lt;/p&gt;

&lt;p&gt;Clojure doesn't deal much with classes, so there isn't much of that kind of introspection, but the Lisp principle of code-as-data enables a kind of introspection that you won't find in Ruby.  The line between compile-time and run-time is very blurry, which enables all kinds of magic.&lt;/p&gt;

&lt;p&gt;Java itself does offer Ruby-style reflection and such, if you need it, but you won't often, while in Clojure land.&lt;/p&gt;

&lt;p&gt;Multimethods (and soon, protocols and &lt;code&gt;defrecord&lt;/code&gt;) let you avoid monkey-patching and get some of the same kinds of &quot;extend a class&quot; things done in a &lt;a href=&quot;http://kirindave.tumblr.com/post/658770511/monkey-patching-gorilla-engineering-protocols-in&quot;&gt;saner and safer way&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;So?&lt;/h1&gt;

&lt;p&gt;Fogus suspects:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ruby programmers being the adventurous lot to begin with, are not satisfied with “halfway to Lisp”. Instead, they want it all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is true in my case.  I like Ruby largely insofar as it borrowed and adapted many great features of Lisp.  It only makes sense that I would like Clojure, which takes most of those things one step further.  Clojure in particular, as a &quot;modern&quot; Lisp with vaguely Ruby-like syntax in certain places, is an obvious choice.&lt;/p&gt;

&lt;p&gt;On top of that, Clojure is fast, thanks to the JVM.  Ruby has JRuby too, but vanilla Ruby is not known for its speed.  Clojure integrates with a REPL in a way that Ruby really doesn't, making interactive development enjoyable.  Clojure is a compiled language, which has benefits for deployment.  And again, there's the whole thread-safety thing.  Clojure is awesome for writing sane, safe multi-threaded programs.  These things are rather appealing.&lt;/p&gt;

&lt;p&gt;I do still use Ruby though.  Ruby is great for scripting, Clojure not so much, thanks to the JVM startup time, among other things.  Ruby can be banged out quickly in any editor, but Clojure isn't much fun to edit in any editor that lacks good paren-matching support and REPL integration.  &lt;/p&gt;

&lt;p&gt;Rubygems offers dead-simple install of a ton of libraries, whereas Clojure is still working out the details of a standard build tool and install tool.  Ruby has a library for anything, and while Clojure can use Java libraries, Java libraries tend to be huge and feature-rich, sometimes too huge for one-off tasks where a small Ruby library is a perfect fit.&lt;/p&gt;</description></item><item><title>Screenshot June 2010</title><link>http://briancarper.net/blog/screenshot-june-2010</link><guid>http://briancarper.net/blog/screenshot-june-2010</guid><pubDate>Tue, 08 Jun 2010 12:27:44 -0700</pubDate><description>&lt;p&gt;I haven't posted one of these in a while.  I've been in an 8-bit kind of mindset for a while:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/screenshots/2010/2010-06-08_2.png&quot;&gt;&lt;img src=&quot;/screenshots/2010/thumbs/2010-06-08_2.png&quot; alt=&quot;Screenshot&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I actually stare at for 8 hours every day:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/screenshots/2010/2010-06-08.png&quot;&gt;&lt;img src=&quot;/screenshots/2010/thumbs/2010-06-08.png&quot; alt=&quot;Screenshot&quot; title=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;KDE4, Buuf icons, QtCurve, wallpaper is from somewhere on the internets.&lt;/p&gt;</description></item><item><title>Emacs isn't for everyone</title><link>http://briancarper.net/blog/emacs-isnt-for-everyone</link><guid>http://briancarper.net/blog/emacs-isnt-for-everyone</guid><pubDate>Tue, 08 Jun 2010 11:26:15 -0700</pubDate><description>&lt;p&gt;Chas Emerick recently posted the results of his &lt;a href=&quot;http://muckandbrass.com/web/display/~cemerick/2010/06/07/Results+from+the+State+of+Clojure%252C+Summer+2010+Survey&quot;&gt;State of Clojure survey&lt;/a&gt;.  It turns out that the (self-selected) group of Clojure-using respondents happen to prefer Emacs as their IDE of choice, eclipsing all other editors by a large margin.&lt;/p&gt;

&lt;p&gt;Chas then has this to say:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I continue to maintain that broad acceptance and usage of Clojure will require that there be top-notch development environments for it that mere mortals can use and not be intimidated by...and IMO, while emacs is hugely capable, I think it falls down badly on a number of counts related to usability, community/ecosystem, and interoperability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As an avid, die-hard Vim and Emacs user for life, I'm going to agree.&lt;/p&gt;

&lt;!--more Read on --&gt;

&lt;h1&gt;Mere mortals?&lt;/h1&gt;

&lt;p&gt;Emacs isn't difficult to learn.  Not in the sense of requiring skill or cleverness.  It is however extremely painful to learn.  I think there's a difference.&lt;/p&gt;

&lt;p&gt;The key word is &lt;em&gt;tedium&lt;/em&gt;.  Learning Emacs is a long process of rote memorization and repetition of commands until they become muscle memory.  If you're smart enough to write programs, you can learn Emacs.  You just have to keep dumping time into the task until you become comfortable.&lt;/p&gt;

&lt;p&gt;Until you're comfortable, you face the unpleasant task of un-learning all of your habits and forming new ones.  And you're trying to do this at the same time you're undertaking another, even harder task: writing programs.  And if you're a new Clojurist, and you're learning Emacs and Clojure from scratch at the same time, well, get the headache medication ready.&lt;/p&gt;

&lt;p&gt;As a programmer and someone who sits in front of a computer 12+ hours a day, I consider myself pretty flexible and capable of picking up a new user interface.  As someone who had been using Vim for years prior to trying Emacs, I considered myself more than capable of learning even a strange and foreign interface.  I'd done it once before.&lt;/p&gt;

&lt;p&gt;But learning Emacs still hurt.  Oh how it hurt.  I blogged while I was learning it, and you can &lt;strong&gt;&lt;a href=&quot;http://briancarper.net/blog/arrrrgh-emacs&quot;&gt;see&lt;/a&gt; &lt;a href=&quot;http://briancarper.net/blog/emacs-pain-continued&quot;&gt;my&lt;/a&gt; &lt;a href=&quot;http://briancarper.net/blog/emacs-undying-hatred&quot;&gt;pain&lt;/a&gt;&lt;/strong&gt; firsthand.  I sometimes hear people say &quot;I tried Emacs for a whole month and I still couldn't get it&quot;.  Well, it took me &lt;em&gt;over a year&lt;/em&gt; to be able to sit down at Emacs and use it fluidly for long periods of time without tripping over the editor.&lt;/p&gt;

&lt;p&gt;To be fair, I'm talking here about using Emacs as a programming environment.  Using Emacs as a Notepad replacement could be learned in short order.  &lt;code&gt;C-x C-f&lt;/code&gt;, &lt;code&gt;C-x C-s&lt;/code&gt;, or use the menus, there you go.  Using it comfortably as a full-fledged IDE is significantly harder and requires you to touch (and master) many more features.  Syntax highlighting, tab-completion, directory traversal and cwd issues, enabling line numbers, version-control integration, build tool integration, Emacs' funky regex syntax for search/replace, Emacs' bizarre kill rings and undo rings, the list goes on.  These things are very flexible in Emacs, which is a great thing, but it's also an impediment to learning how to configure and use them.  There's no getting around the time investment.&lt;/p&gt;

&lt;p&gt;And it's not just a matter of learning some new keyboard shortcuts.  There's a new vocabulary to learn.  You don't open files, you visit them.  What's a buffer?  What's a window?  (Not what you think it is.)  What's a point?  What's a mark?  Kill?  Yank?  &quot;&lt;em&gt;Apropos&lt;/em&gt;&quot;?  Huh?  &lt;code&gt;C-c M-o&lt;/code&gt; means what exactly?  My keyboard doesn't have a Meta key.  Yeah, you can use CUA mode and get your modernized Copy/Cut/Paste shortcuts back, but that's the tip of the iceberg.  It's hard even to know where to begin looking for help.&lt;/p&gt;

&lt;p&gt;Yeah, Emacs came first, before our more common and more modern conventions were established, and that explains why it's so different.  That doesn't change the fact that Emacs today is a strange beast.&lt;/p&gt;

&lt;h1&gt;Community and ecosystem&lt;/h1&gt;

&lt;p&gt;Personally I find the Emacs community to be a pretty nice bunch.  In the highest tradition of hackerdom and open source software, Emacs users seem to be eager and willing to share their elisp snippets and bend over backwards to help other people learn the editor.  I got lots of help when I was struggling and learning Emacs.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://www.emacswiki.org/&quot;&gt;Emacs wiki&lt;/a&gt; is an awesome resource.  The &lt;a href=&quot;http://www.gnu.org/software/emacs/manual/emacs.html&quot;&gt;official documentation&lt;/a&gt; is so complete (and so long) that it leaves me speechless sometimes.  And there are a million 3rd-party scripts for it.  Whatever you want Emacs to do is generally a short google away.&lt;/p&gt;

&lt;p&gt;If there's anything wrong with the Emacs community, it'd be people who take Emacs evangelism overboard.  The answer to &quot;&lt;strong&gt;&lt;em&gt;I don't want to have to use Emacs to use your language&lt;/em&gt;&lt;/strong&gt;&quot; can't be &quot;&lt;em&gt;Be quiet and learn more Emacs&lt;/em&gt;&quot;, or &quot;&lt;em&gt;If you're too dumb to learn Emacs, go away&lt;/em&gt;&quot;.  In some communities there is certainly some of that.  But thankfully I don't see it much in the Clojure community.  Let's hope it stays that way.&lt;/p&gt;

&lt;h1&gt;Interoperability&lt;/h1&gt;

&lt;p&gt;Once someone spends the time to write a suitable amount of elisp, Emacs can interoperate with anything.  I think so many people use SLIME for Clojure development precisely because it interoperates so darned well with Lisps.  SLIME is amazing.  You probably can't beat &lt;a href=&quot;http://www.emacswiki.org/emacs/ParEdit&quot;&gt;Paredit&lt;/a&gt; either, and Emacs' flexibility is precisely what makes things like Paredit possible.&lt;/p&gt;

&lt;p&gt;The problem is the amount of time you have to spend to get that interoperability set up and to learn how to use it.  After two years of using Emacs and Clojure together, every once in a while I still find myself bashing my face on my desk trying to get the latest SLIME or swank to work just right, or trying to get a broken key binding fixed, or tweaking some other aspect of Emacs that's driving me crazy.  One day, curly braces stopped being recognized as matched pairs by Paredit.  Why?  No idea; I fixed it, but it was a half hour of wasted time.&lt;/p&gt;

&lt;p&gt;Emacs is good at integrating with Git too.  So good that there are four or five different Emacs-Git libraries, each with a different interface and feature set.  I gave up eventually and went back to using the command line.  (You can embed a shell / command line right in Emacs.  There are three or four different libraries to do that too.)&lt;/p&gt;

&lt;p&gt;The wealth of options of ways to do things in Emacs is simultaneously a good thing, overwhelming and confusing.  If all you want is something that works and gets out of your way, too many options can be worse than one option, even if that one option isn't entirely ideal.&lt;/p&gt;

&lt;p&gt;Emacs' Java interop, I know nothing about.  Almost certainly, Emacs can come close to a modern Java IDE for fancy features like tab-completion and document lookups and project management.  But how long is it going to take you to figure out that tab-completion is called &lt;code&gt;hippie-expand&lt;/code&gt; in Emacs?  That and a million other surprises await you.&lt;/p&gt;

&lt;h1&gt;What's my point?&lt;/h1&gt;

&lt;p&gt;There was a pithy quote floating around on Twitter a while back (I think quoting Rich Hickey):&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;One possible way to deal with being unfamiliar with something is to become familiar with it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's true, and you could say that of Emacs.  I strongly believe that when it comes to computers, &lt;a href=&quot;http://briancarper.net/blog/lisp-syntax-doesnt-suck&quot;&gt;there's no such thing as &quot;intuitive&quot;&lt;/a&gt;.  There's stuff you've already spent a lot of time getting used to, and there's stuff you haven't.&lt;/p&gt;

&lt;p&gt;But certain things require more of a time investment than others.  Could I learn Clojure if all the keywords were in Russian or Chinese instead of my native English?  Sure, but it'd take me a long time.  I'd certainly have to have a good reason to attempt it.&lt;/p&gt;

&lt;p&gt;I learned Emacs partly because it was hard.  I saw it as a challenge.  It was fun, yet painful, but more pain, more glory.  Mastering it makes me feel like I've accomplished something.  I'd encourage other people to learn Emacs and Vim too.  I think the benefits of knowing them outweigh the cost and time investment of learning them.&lt;/p&gt;

&lt;p&gt;But I didn't learn Emacs with the goal of being productive.  I learned it for the same reason some people build cars in their garages, while most people just buy one and drive it to and from work every day.  I learned Emacs because I love programming and I love playing with toys, and Vim or Emacs are as nice a toy as I could ask for.  (I love programming enough to form strong opinions and write huge blog posts about text editors.)  For me, productivity was a beneficial side-effect.&lt;/p&gt;

&lt;p&gt;There are only so many hours in a day.  There are a lot of other challenges to conquer, some of which offer more tangible benefits than Emacs mastery would get you.  Mastering an arcane text editor isn't necessarily going to be on the top of the list of everyone's goals in life, especially when there are other editors that are easier to use and give you a significant subset of what Emacs would give you.  We have to pick our battles.&lt;/p&gt;

&lt;p&gt;So I understand when people say they don't want to learn Emacs.  I think maybe so many Clojurists use Emacs right now because we're still in the early adopter stage.  If you're using Clojure today, you're probably pretty enthusiastic about programming.  You're likely invested enough to be willing to burn the required time to learn Emacs.&lt;/p&gt;

&lt;p&gt;If Clojure becomes &quot;big&quot;, there are going to be a lot of casual users.  A casual user of Clojure isn't going to learn Emacs.  They're going to silently move on to another language.  And I really think that new blood is vital to the strength of a community and necessary for the continued healthy existence of a programming language.&lt;/p&gt;

&lt;p&gt;So Clojure does need alternatives.  I'll stick with Emacs myself, but there should be practical alternatives.  I'd encourage the Clojure community to continue to support and enjoy Emacs, but don't push it too hard.&lt;/p&gt;</description></item><item><title>Emacs: Yank lines as lines</title><link>http://briancarper.net/blog/emacs-yank-lines-as-lines</link><guid>http://briancarper.net/blog/emacs-yank-lines-as-lines</guid><pubDate>Fri, 21 May 2010 12:07:13 -0700</pubDate><description>&lt;p&gt;One thing nice about Vim is manipulating whole lines at a time.  &lt;code&gt;dd&lt;/code&gt; deletes a line (including trailing newline), regardless of where the cursor is on the line.  Then, &lt;code&gt;p&lt;/code&gt; puts that line (with its newline) as a new line after the current line, and &lt;code&gt;P&lt;/code&gt; puts it above the current line, again regardless of where your cursor is at the moment.  (It also jumps the cursor to the beginning of the text you just inserted, which is nice.)&lt;/p&gt;

&lt;p&gt;Emacs has &lt;code&gt;kill-whole-line&lt;/code&gt; (&lt;code&gt;C-S-Backspace&lt;/code&gt;) which is like Vim's &lt;code&gt;dd&lt;/code&gt;.  But I didn't find an equivalent of &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;P&lt;/code&gt;.  So here's my version:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defun yank-with-newline ()
  &quot;Yank, appending a newline if the yanked text doesn't end with one.&quot;
  (yank)
  (when (not (string-match &quot;\n$&quot; (current-kill 0)))
    (newline-and-indent)))

(defun yank-as-line-above ()
  &quot;Yank text as a new line above the current line.

Also moves point to the beginning of the text you just yanked.&quot;
  (interactive)
  (let ((lnum (line-number-at-pos (point))))
    (beginning-of-line)
    (yank-with-newline)
    (goto-line lnum)))

(defun yank-as-line-below ()
  &quot;Yank text as a new line below the current line.

Also moves point to the beginning of the text you just yanked.&quot;
  (interactive)
  (let* ((lnum (line-number-at-pos (point)))
         (lnum (if (eobp) lnum (1+ lnum))))
    (if (and (eobp) (not (bolp)))
        (newline-and-indent)
      (forward-line 1))
    (yank-with-newline)
    (goto-line lnum)))

(global-set-key &quot;\M-P&quot; 'yank-as-line-above)
(global-set-key &quot;\M-p&quot; 'yank-as-line-below)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just one more step along the path to Vimmify my Emacs setup.  Emacs has some weird edge cases because you can move the cursor one &quot;line&quot; past the last real line in the file.  But I think I worked out something comfortable for myself.&lt;/p&gt;

&lt;p&gt;PS: I've written about this before, but if you use &lt;code&gt;C-S-Backspace&lt;/code&gt; a lot in Emacs on Linux, I highly recommend putting this into your X11 config:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Option &quot;DontZap&quot; &quot;True&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's really easy to mix up &lt;code&gt;C-S-Backspace&lt;/code&gt; and &lt;code&gt;C-M-Backspace&lt;/code&gt; (the latter of which kills your X server).  It's not fun to mix those up.  Not fun at all.&lt;/p&gt;

&lt;p&gt;PPS: &lt;a href=&quot;http://stackoverflow.com/questions/2173324/emacs-equivalents-of-vims-dd-o-o&quot;&gt;This thread on Stack Overflow&lt;/a&gt; has some Emacs equivalents of Vim's &lt;code&gt;o&lt;/code&gt; and &lt;code&gt;O&lt;/code&gt; which are pretty nice too.&lt;/p&gt;</description></item></channel></rss>
