<?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>Shuffle lines in Vim</title><link>http://briancarper.net/blog/580/shuffle-lines-in-vim</link><guid>http://briancarper.net/blog/580/shuffle-lines-in-vim</guid><pubDate>Thu, 07 Jul 2011 14:07:41 -0700</pubDate><description>&lt;p&gt;In a pinch, I needed to randomize the order of a few thousand lines of plain text.  In Linux you can just pipe the file through &lt;code&gt;sort&lt;/code&gt;, even right inside Vim:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:%!sort -R
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But I was stuck on Windows.  And I don't know how to randomize a file in native Vim script.  But doing it in Ruby is pretty easy, and luckily, Vim has awesome Ruby support.  Tne minutes' work and a few peeks at &lt;code&gt;:h ruby&lt;/code&gt; and we have a successful, working kludge:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function! ShuffleLines()
ruby &amp;lt;&amp;lt; EOF
    buf = VIM::Buffer.current
    firstnum =  VIM::evaluate('a:firstline')
    lastnum = VIM::evaluate('a:lastline')
    lines = []
    firstnum.upto(lastnum) do |lnum|
      lines &amp;lt;&amp;lt; buf[lnum]
    end
    lines.shuffle!
    firstnum.upto(lastnum) do |lnum|
      buf[lnum] = lines[lnum-firstnum]
    end
EOF
endfunction
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;2011-07-07 23:32 - Edited to remove a superfluous line.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2011-07-09 21:33 - Wrong parameter for &lt;code&gt;sort&lt;/code&gt;, oops.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Keyword Arguments: Ruby, Clojure, Common Lisp</title><link>http://briancarper.net/blog/579/keyword-arguments-ruby-clojure-common-lisp</link><guid>http://briancarper.net/blog/579/keyword-arguments-ruby-clojure-common-lisp</guid><pubDate>Fri, 24 Jun 2011 17:22:48 -0700</pubDate><description>&lt;p&gt;And suddenly I return to blogging, rising from the ashes like some kind of zombie phoenix.  Turns out &lt;a href=&quot;http://oreilly.com/catalog/0636920013754/&quot;&gt;writing a book&lt;/a&gt; is a good absorber of time, like some sort of heavy-duty temporal paper towel.  Now that I've gotten the terrible similes out of my system, let's talk about keyword arguments, one of my favorite features in any language that supports them.&lt;/p&gt;

&lt;p&gt;Ruby, Clojure, and Common Lisp are all languages I enjoy to some degree, and they all have keyword arguments.  Let's explore how keyword args differ in those languages.&lt;/p&gt;

&lt;!--more Read about keyword arguments. --&gt;

&lt;h1&gt;Why keyword arguments?&lt;/h1&gt;

&lt;p&gt;Why are keyword arguments good?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can omit arguments.&lt;/li&gt;
&lt;li&gt;You can supply arguments in an arbitrary order.&lt;/li&gt;
&lt;li&gt;Arguments are labeled, so you know what argument means what. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Positional arguments require mentally lining up the 7th argument in your function call with the 7th argument in the function signature, and so on.  Keyword arguments become more and more attractive the more arguments you have in your function signature.&lt;/p&gt;

&lt;p&gt;Keyword arguments trade a bit of verbosity for added explicitness, clarity and reduced mental burden.  (Kind of like Lisps do overall.  Fancy that.)&lt;/p&gt;

&lt;h1&gt;Ruby&lt;/h1&gt;

&lt;p&gt;Ruby doesn't have special support for keyword arguments.  But Ruby likes its hashes, so you can just pass one in as an argument to a function.  As some syntax sugar, if you pass a &quot;flat&quot; list of &lt;code&gt;:key =&amp;gt; val&lt;/code&gt; pairs, Ruby slurps them all together and stuffs them into a hash for you.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;def foo(arg)
  p arg
end

foo({:x =&amp;gt; 123})                  # =&amp;gt; {:x=&amp;gt;123}
foo(:x =&amp;gt; 123)                    # =&amp;gt; {:x=&amp;gt;123}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With even more added sugar, you can leave off the parens in Ruby function calls.  So this is pretty common in Ruby:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;foo :x =&amp;gt; 123                     # =&amp;gt; {:x=&amp;gt;123}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;How nice and punctuation-less.  But then things get ugly.  What about this?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;foo {:x =&amp;gt; 123}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That won't even compile.  Ruby thinks &lt;code&gt;{:x =&amp;gt; 123}&lt;/code&gt; is a code block, and a bare key/value pair isn't valid syntax as the first thing in a code block.  You need the parens.  A bit unfortunate, but it gets worse...&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;def bar(arg1,arg2)
  puts &quot;#{arg1} #{arg2}&quot;
end

bar :x =&amp;gt; 123, :y =&amp;gt; 456          # Runtime error
bar {:x =&amp;gt; 123}, {:y =&amp;gt; 456}      # Won't compile
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the first example, all of the key/value pairs are slurped into one hash and end up in &lt;code&gt;arg1&lt;/code&gt;.  There's nothing left for &lt;code&gt;arg2&lt;/code&gt;, so you get a &quot;wrong number of arguments&quot; exception.  The second example won't even compile, of course, because again Ruby thinks &lt;code&gt;{:x =&amp;gt; 123}&lt;/code&gt; must be a code block with invalid syntax.&lt;/p&gt;

&lt;p&gt;It gets worse if you change the argument list for &lt;code&gt;bar&lt;/code&gt; slightly...&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;def bar2(arg1 = {}, arg2 = {})
  puts &quot;#{arg1} #{arg2}&quot;
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This way, you don't even have to supply any arguments.  This is nice, if all of your argument are optional.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;bar2    # =&amp;gt; {} {}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now suppose you want &lt;code&gt;arg1&lt;/code&gt; to be &lt;code&gt;{:x =&amp;gt; 123}&lt;/code&gt;, and &lt;code&gt;arg2&lt;/code&gt; to be &lt;code&gt;{:y =&amp;gt; 456}&lt;/code&gt;.  You might naively try this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;bar2 :x =&amp;gt; 123, :y =&amp;gt; 456         # =&amp;gt; {:x=&amp;gt;123, :y=&amp;gt;456} {}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Oops, it all got dumped into &lt;code&gt;arg1&lt;/code&gt;, and now instead of a missing argument error, &lt;code&gt;arg2&lt;/code&gt; silently ends up with a default, empty hash.  You have to explicitly pass in an empty value for &lt;code&gt;arg1&lt;/code&gt; so that everything is slurped into &lt;code&gt;arg2&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;bar2 {}, :x =&amp;gt; 123, :y =&amp;gt; 456     # =&amp;gt; WRONG!  Ruby thinks {} is a code block again.

bar2({}, :x =&amp;gt; 123, :y =&amp;gt; 456)    # =&amp;gt; {} {:x=&amp;gt;123, :y=&amp;gt;456}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So much for syntax sugar.  You might think you'd be unlikely to find this kind of thing in the wild, but in Ruby on Rails for example, there are quite a few functions whose argument lists look exactly like this.  One signature for &lt;code&gt;link_to&lt;/code&gt; is:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;link_to(body, url_options = {}, html_options = {})
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So...&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;link_to &quot;foo&quot;, :controller =&amp;gt; :x                          # OK
link_to &quot;foo&quot;, :controller =&amp;gt; :x, :class =&amp;gt; &quot;css_class&quot;   # WRONG! 
link_to &quot;foo&quot;, {:controller =&amp;gt; :x}, :class =&amp;gt; &quot;css_class&quot; # OK
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What about support for default arguments?  We might want to say that if you didn't pass in an &lt;code&gt;:x&lt;/code&gt; argument, we want it to have some default value.  You might think this would work:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;def baz(x = {:x =&amp;gt; 123})
   p x
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But you would be sadly mistaken.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;baz                               # =&amp;gt; {:x=&amp;gt;123}
baz :y =&amp;gt; 456                     # =&amp;gt; {:y=&amp;gt;456} ... oops
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ruby doesn't merge your keyword arguments into the map in the parameter list.  It uses that map if you don't supply any arguments, otherwise your map replaces the default entirely.  So to get default arguments, you need something like&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;def baz2(args = {})
  args = {:x =&amp;gt; 123}.merge(args)
  p args
end

baz2                              # =&amp;gt; {:x=&amp;gt;123}
baz2 :x =&amp;gt; 555                    # =&amp;gt; {:x=&amp;gt;555}
baz2 :y =&amp;gt; 456                    # =&amp;gt; {:x=&amp;gt;123, :y=&amp;gt;456}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Kind of messy, but that's OK.&lt;/p&gt;

&lt;p&gt;One last subtle ambiguity in Ruby is determining whether someone passed a &lt;code&gt;nil&lt;/code&gt; argument for a keyword explicitly, or omitted a keyword entirely.  It might make a difference in some circumstances.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: ruby&quot;&gt;def quux(args={})
  p args.include? :x
  args = {:x =&amp;gt; nil}.merge(args)
  p args
end

quux                              # true, {:x=&amp;gt;nil}
quux :x =&amp;gt; nil                    # false, {:x=&amp;gt;nil}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pretty messy, but such is life.&lt;/p&gt;

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

&lt;p&gt;Part of the fun of Lisps is lack of ambiguity.  Everything is spelled out in all its parenthesized glory.  In Clojure, when you call a function like &lt;code&gt;(foo :x 123)&lt;/code&gt;, Clojure requires you to specify what you want to happen with those arguments.&lt;/p&gt;

&lt;p&gt;It used to be that Clojure didn't have much support for keyword args at all.  Clojure did have support for allowing variable numbers of arguments to functions though.  So in the beginning, people used to slurp all of their arguments together into a list, and then turn it into a map inside the function.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (defn foo [&amp;amp; args]
        (let [args (apply hash-map args)]
          (prn args)))
#'user/foo
user&amp;gt; (foo)
{}
user&amp;gt; (foo :x 123)
{:x 123}
user&amp;gt; (foo :x 123 :y 456)
{:y 456, :x 123}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That worked.  It still works today.  But nowadays there's a better way.  Why not slurp your arguments directly into a hash-map?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (defn foo [&amp;amp; {:as args}]
        (prn args))
#'user/foo
user&amp;gt; (foo)
nil
user&amp;gt; (foo :x 123)
{:x 123}
user&amp;gt; (foo :x 123 :y 456)
{:y 456, :x 123}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is an example of &lt;em&gt;destructuring&lt;/em&gt;.  In this case, all of our arguments are slurped into a list (thanks to &lt;code&gt;&amp;amp;&lt;/code&gt;), then this list is matched against our destructuring pattern, in this case &lt;code&gt;{:as args}&lt;/code&gt;.  &lt;code&gt;:as&lt;/code&gt; says to take everything in the list, turn it into a map and give the resulting map the name &lt;code&gt;args&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This isn't a special feature of &lt;code&gt;defn&lt;/code&gt;.  Destructuring works anywhere you're setting up bindings, for example in &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;doseq&lt;/code&gt; etc.  Like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;user&amp;gt; (let [{:as args} (list :x 123 :y 456)] args)
{:y 456, :x 123}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It just so happens that &lt;code&gt;&amp;amp;&lt;/code&gt; creates the list for you, out of the arguments you pass.&lt;/p&gt;

&lt;p&gt;Destructuring does lots more than that though.  We can immediately pull out the values for keywords we care about, so they'll be bound to names in our function body.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (defn foo [&amp;amp; {:keys [x y z]}]
        (prn x y z))
#'user/foo

user&amp;gt; (foo :z 123 :x 456)
456 nil 123
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It's certainly a bit more verbose than Ruby in the function signature, but it lacks ambiguity and it saves you some repetition in the function body. &lt;/p&gt;

&lt;p&gt;Clojure's approach also has the benefit of specifying directly in the function signature which keywords you're expecting.  In a smart editor, like Emacs, you get an indicator of what kinds of keywords you should be passing in.   See at the bottom?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/clojure/function-signature.png&quot; alt=&quot;Emacs function signature display&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is also available at the Clojure REPL via the &lt;code&gt;doc&lt;/code&gt; function.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (doc foo)
-------------------------
user/foo
([&amp;amp; {:keys [a b c]}])
  nil
nil
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There's no better documentation than live, built-in documentation.  There's nothing more distracting when programming than context shifts, and having to dig into a web browser to check a function signature is a huge mental page fault.&lt;/p&gt;

&lt;p&gt;What about default values?  Sure.  You can use &lt;code&gt;:or&lt;/code&gt; to specify defaults for some or all of your keywords.  This works much more like I'd expect, compared to Ruby.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (defn foo [&amp;amp; {:keys [x y z] :or {x 1 y 2 z 3}}]
        (prn x y z))
#'user/foo

user&amp;gt; (foo :y 555)
1 555 3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What about determining whether the user passed &lt;code&gt;nil&lt;/code&gt; for a keyword or whether they omitted the keyword entirely?  For that, you have to resort to testing the argument map for the existence of the key, which isn't fun, but at least it's possible.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;user&amp;gt; (defn foo [&amp;amp; {:keys [x y z] 
                    :or {x 1 y 2 z 3}
                    :as args}]
        (prn x y z)
        (prn args)
        (doseq [k [:x :y :z]]
          (println &quot;Contains&quot; k &quot;=&amp;gt;&quot; (contains? args k))))
#'user/foo

user&amp;gt; (foo :x 1 :y 2)
1 2 3
{:x 1, :y 2}
Contains :x =&amp;gt; true
Contains :y =&amp;gt; true
Contains :z =&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Why do I keep mentioning this?  See Common Lisp below.&lt;/p&gt;

&lt;p&gt;In any case, our parameter list is becoming huge and unweildy.  The first time I saw sample Clojure code like this, I almost did a spit-take.  But after a bit of getting used to, I'm finding that this syntax is pretty comfortable.&lt;/p&gt;

&lt;p&gt;The simplest case may not be as concise as Ruby, but aside from lack of ambiguity, the benefit of Clojure's approach is being able to safely do powerful (and borderline insane) things, like nested destructuring.  And this works everywhere you're setting up a binding.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: clojure&quot;&gt;(defn ow-my-eyes [a &amp;amp; {[b {:keys [x y]}
                        &amp;amp; {[g h] :r
                           :keys [p q]
                           :or {q 123}}]
                       :x}]
  (prn a b g h p q x y))



user&amp;gt; (ow-my-eyes 444 :x [1 {:x 555 :y 666} :p 3 :r [888 999]])
444 1 888 999 3 123 555 666
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you wrote code like that in real life, you'd likely be defenestrated, but at least you know you can do it.  And destructuring in Clojure could likely be extended even further in the future, if someone came up with a use case for something that isn't supported.&lt;/p&gt;

&lt;p&gt;Official documentation for all of this is &lt;a href=&quot;http://clojure.org/special_forms&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;Common Lisp&lt;/h1&gt;

&lt;p&gt;When it comes to keyword arguments, Common Lisp supports mostly everything that Clojure does, and some things it doesn't.  CL was likely a big inspiration for Clojure's destructuring.  I highly recommend reading Practical Common Lisp to learn more about CL &lt;a href=&quot;http://www.gigamonkeys.com/book/functions.html&quot;&gt;keyword arguments&lt;/a&gt; and &lt;a href=&quot;http://www.gigamonkeys.com/book/beyond-lists-other-uses-for-cons-cells.html&quot;&gt;list destructuring&lt;/a&gt;.  (Read the rest of the book while you're at it.)&lt;/p&gt;

&lt;p&gt;Keyword arguments in CL look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; (defun foo (&amp;amp;key x (y 123) (z 456 z-supplied-p))
    (pprint (list x y z z-supplied-p)))
FOO

&amp;gt; (foo)
(NIL 123 456 NIL)

&amp;gt; (foo :z nil)             ; note, z-supplied-p tells us whether z was omitted or not
(NIL 123 NIL T)

&amp;gt; (foo :z 555 :x 666 :y 777)
(666 777 555 T)

&amp;gt; (foo :x 123)
(123 123 456 NIL)

&amp;gt; (foo :x 555)
(555 123 456 NIL)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Yeah, there's direct support for distinguishing supplied keys with nil values, and un-supplied keys.  That's pretty nice.&lt;/p&gt;

&lt;p&gt;Common Lisp also supports insane things like having keyword arguments' default values be a function of other arguments in the parameter list.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; (defun bar (&amp;amp;key (x 123) (y (+ x 1000)))
    (pprint (list x y)))
BAR

&amp;gt; (bar)
(123 1123)

&amp;gt; (bar :x 5)
(5 1005)

&amp;gt; (bar :x 5 :y 6)
(5 6)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Clojure destructuring can't do this by default, so you'd have to resort to a &lt;code&gt;let&lt;/code&gt; in the function body.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(defn bar [&amp;amp; {:keys [x y] :or {x 123}}]
  (let [y (or y (+ x 1000))]
    (prn x y)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There's nothing stopping Clojure's destructuring from being patched to support this, of course.  But this isn't a feature I've ever found myself wanting particularly badly.&lt;/p&gt;

&lt;p&gt;As for how to destructure CL keyword arguments into a vector or hash-map, CL doesn't directly support doing it in function parameter lists like Clojure does.  (Though there's nothing stopping someone from throwing together a reader macro to let CL do this, of course.)  This isn't really surprising, because CL loves its cons cells, while Clojure embraces maps (and vectors and sets etc.), offering them default reader syntax and lots of other built-in support.&lt;/p&gt;

&lt;p&gt;So there you have it.  Keyword arguments.  Use them.  Love them.&lt;/p&gt;

&lt;p&gt;Lessons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sugar can be bad for your health.&lt;/li&gt;
&lt;li&gt;Ambiguous, bad.  Explicit, good.&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>org-mode is awesome</title><link>http://briancarper.net/blog/577/org-mode-is-awesome</link><guid>http://briancarper.net/blog/577/org-mode-is-awesome</guid><pubDate>Thu, 20 Jan 2011 16:14:44 -0800</pubDate><description>&lt;p&gt;I've seen &lt;a href=&quot;http://orgmode.org/&quot;&gt;org-mode&lt;/a&gt; for Emacs mentioned very frequently around the interwebs, so it went into my mental queue of topics to learn.  It finally bubbled to the top this week, so I took a look.&lt;/p&gt;

&lt;h1&gt;Organizer?  Nah.&lt;/h1&gt;

&lt;p&gt;As an organizer/calendar, well, I doubt I'll need it.  Enforced use of MS Outlook is mandated by work.  My Post-it-notes-all-over-my-desk method of organization will also continue to serve me well.&lt;/p&gt;

&lt;p&gt;There are some nice agenda-related shortcuts that are probably worth using though, like typing &lt;code&gt;C-c .&lt;/code&gt; to enter a datestamp, like &lt;code&gt;&amp;lt;2011-01-20 Thu&amp;gt;&lt;/code&gt;.  Then you can increment or decrement it one year/month/day at a time via &lt;code&gt;S-up&lt;/code&gt; and &lt;code&gt;S-down&lt;/code&gt;.  I like this.&lt;/p&gt;

&lt;h1&gt;Plaintext editor?  Yes!&lt;/h1&gt;

&lt;p&gt;As a plaintext outline and table editor... wow.  org-mode rocks.  Do you know how many hours of my life could have been saved by having a good ASCII table/bullet-list editor?  org-mode lines everything up and keeps it all nice and neat for you.&lt;/p&gt;

&lt;p&gt;You can also make plaintext check boxes and check/uncheck them.  And you can insert hyperlinks and footnotes, and click them to open web pages or jump back and forth between footnote and reference.&lt;/p&gt;

&lt;p&gt;There are ways to collapse and expand outlines, search for items and only display those items, and so on.  The &lt;a href=&quot;http://orgmode.org/guide/index.html&quot;&gt;documentation&lt;/a&gt; for org-mode is very clear and took me less than an hour to read through.  All-in-all a pleasant experience.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;* Agenda
** Things to learn
1. [X] Clojure
2. [X] org-mode (see [fn:diagram1])
3. [ ] Haskell
4. [ ] Japanese
   1. [X] Hiragana
   2. [X] Katakana
   3. [ ] Kanji
5. [ ] The true meaning of friendship

* Footnotes
[fn:diagram1]

| Task                              | Annoyance (1-10) |
|-----------------------------------+------------------|
| Making ASCII tables by hand       |              9.5 |
| Making ASCII bullet lists by hand |              7.2 |
| Using org-mode                    |              0.4 |
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It looks nice plastered into my blog, but you don't get a real idea of how many cool things you can do with it until you open it in Emacs and start shuffling items around, bumping them up/down a level in headlines, creating properly-numbered bullet items with one key, and seeing the columns in the table auto-resize as you type.&lt;/p&gt;

&lt;p&gt;I also highly recommend putting &lt;code&gt;(setq org-startup-indented t)&lt;/code&gt; into &lt;code&gt;.emacs&lt;/code&gt; to make everything look pretty on-screen.  It still saves as the simple plaintext above, but it looks like this in Emacs:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/emacs/org-mode.png&quot; alt=&quot;org-mode&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I can definitely see using org-mode for TODO files in some of my projects.  (You can mark entries as TODO (just by typing &lt;code&gt;TODO&lt;/code&gt; in front), and then toggle between TODO/DONE via &lt;code&gt;C-c C-t&lt;/code&gt;.)   I can also see using it as a general-purpose note-taker.&lt;/p&gt;

&lt;p&gt;org-mode also has a &lt;a href=&quot;http://mobileorg.ncogni.to/doc/&quot;&gt;mobile version&lt;/a&gt; for iPhone and Android, synced via WebDAV or Dropbox, so you can org-mode on your phone while you're driving to the grocery store&lt;sup id=&quot;fnref:dontdothis&quot;&gt;&lt;a href=&quot;#fn:dontdothis&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.  Again I don't really need this, but there it is.&lt;/p&gt;

&lt;h1&gt;The joy of plaintext&lt;/h1&gt;

&lt;p&gt;Plaintext is awesome.&lt;/p&gt;

&lt;p&gt;It's the universal file format.  It's readable and writeable by scripting langauges, terminals, text editors, IDEs, word processors, web browsers, even lowly humans.&lt;/p&gt;

&lt;p&gt;Plaintext's one shortcoming is its lack of structure.  It's just a bunch of letters.  It doesn't have a color, it doesn't have a style, it doesn't line up into columns without a lot of effort.  There's nothing stopping you from opening a parenthesized list and forgetting the closing paren.&lt;/p&gt;

&lt;p&gt;Computers don't care about these problems, but humans are bad at producting plaintext by hand, and bad at editing it once it's produced.  Our clumsy, stumpy fingers and inconsistent, chaotic brains can't handle the freedom.&lt;/p&gt;

&lt;p&gt;Emacs (and Vim) are awesome because they let you do magical things to plaintext.  They enforce structure.  They provide shortcuts so you can get your plainext right the first time.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;[ ]&lt;/code&gt; is just two braces and a space, but org-mode lets me hit &lt;code&gt;C-c C-c&lt;/code&gt; and turn the space into an &lt;code&gt;X&lt;/code&gt;.  This may seem banal, hardly worth caring about, but add to this shortcut thousands upon thousands of others.  Things like org-mode, or &lt;a href=&quot;http://www.emacswiki.org/emacs/ParEdit&quot;&gt;paredit&lt;/a&gt;, or all of Vim's built-in magic... it all adds up to something wonderful.&lt;/p&gt;

&lt;p&gt;And best of all, you always still have the option of manually keyboarding over and typing that &lt;code&gt;X&lt;/code&gt; between the braces yourself.  It's still just plaintext underneath.  So you end up with the best of both worlds.&lt;/p&gt;&lt;div class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;fn:dontdothis&quot;&gt;&lt;p&gt;I do not recommend using org-mode while driving, for public safety reasons. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:dontdothis&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description></item><item><title>2010 in review</title><link>http://briancarper.net/blog/574/2010-in-review</link><guid>http://briancarper.net/blog/574/2010-in-review</guid><pubDate>Wed, 05 Jan 2011 10:10:03 -0800</pubDate><description>&lt;p&gt;Another year down the drain.  A good year, in the end.&lt;/p&gt;

&lt;h1&gt;2010 Geek Achievements&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Wrote some code...
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/briancarper/cow-blog&quot;&gt;cow-blog&lt;/a&gt; - The engine running this blog.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/briancarper/oyako&quot;&gt;oyako&lt;/a&gt; - Clojure ORM library.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/briancarper/gaka&quot;&gt;gaka&lt;/a&gt; - CSS compiler for Clojure&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Finished a huge project for work, my first AJAX-y web app (in Rails).  That was fun, albeit stressful.&lt;/li&gt;
&lt;li&gt;Learned a lot of git.&lt;/li&gt;
&lt;li&gt;Learned a lot of Clojure.  &lt;/li&gt;
&lt;li&gt;Learned a lot of Emacs.&lt;/li&gt;
&lt;li&gt;Learned a lot of Javascript.&lt;/li&gt;
&lt;li&gt;Learned a lot of  PostgreSQL.  It's good to be free of MySQL.&lt;/li&gt;
&lt;li&gt;Switched to ZSH.  This was a good switch.&lt;/li&gt;
&lt;li&gt;Tried to learn a lot of Japanese, but kind of fizzled out at the end of the year.&lt;/li&gt;
&lt;li&gt;Alllllllmost got a Clojure gold badge on &lt;a href=&quot;http://stackoverflow.com/tags/clojure/topusers&quot;&gt;Stack Overflow&lt;/a&gt;.  I'll get it soon though.  Not losing any sleep over it either way.&lt;/li&gt;
&lt;li&gt;Read a lot of books.  The best: probably Feynman's books of anecdotes.&lt;/li&gt;
&lt;li&gt;Blogged a bit.  Got an article in &lt;a href=&quot;http://hackermonthly.com/issue-3.html&quot;&gt;Hacker Monthly&lt;/a&gt;.  Was flamed repeatedly.  Learned a lot in the process.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;2010 Non-Geek Achievements&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Immigrated to Canada.  A good move, without a doubt.&lt;/li&gt;
&lt;li&gt;Lost 25ish lbs. :)&lt;/li&gt;
&lt;li&gt;Learned how to cook better.&lt;/li&gt;
&lt;li&gt;My most important achievement from 2010 is actually non-geek: I finally obtained a bit of an offline social life.  This is not an easy task for one such as myself.&lt;/li&gt;
&lt;li&gt;Continued to learn to appreciate good beer.  Longwood Dunkelweizen, mmm.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;2010 Failures&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Did not blog enough.&lt;sup id=&quot;fnref:sociallife&quot;&gt;&lt;a href=&quot;#fn:sociallife&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;Did not write enough code.&lt;sup id=&quot;fnref:sociallife&quot;&gt;&lt;a href=&quot;#fn:sociallife&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;Missed the &lt;a href=&quot;http://clojure-conj.org/&quot;&gt;first Clojure Conj&lt;/a&gt;.  Maybe next year.&lt;/li&gt;
&lt;li&gt;Re-gained 10ish lbs. :( &lt;sup id=&quot;fnref:fat&quot;&gt;&lt;a href=&quot;#fn:fat&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;Plans for 2011&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Re-lose 25ish pounds.  I'd like to reach the weight I had in college.&lt;/li&gt;
&lt;li&gt;Finish my rewrite of oyako.  I have ambitious plans for it, if I can just find the time.&lt;/li&gt;
&lt;li&gt;Finish my rewrite of cow-blog to match oyako.&lt;/li&gt;
&lt;li&gt;Keep working on the RPG my wife and I are creating (in Clojure).&lt;/li&gt;
&lt;li&gt;Attend the next Clojure Conj, I hope.&lt;/li&gt;
&lt;li&gt;Learn more Clojure.&lt;/li&gt;
&lt;li&gt;Learn Haskell?  Trying and failing to learn Haskell has become somewhat of a tradition, no sense stopping now.&lt;/li&gt;
&lt;li&gt;Learn all 2000+ jouyou kanji by the end of the year.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;em&gt;Supar sekrit projekt.&lt;/em&gt;&lt;/strong&gt;  But I haven't signed the contract for it yet so I won't talk about it until I do.&lt;/li&gt;
&lt;li&gt;Maintain social life at acceptable levels.&lt;/li&gt;
&lt;li&gt;Buy a house.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I feel like I have solid plans for completing each of these things.  Blogging more often and finishing oyako are high on my list of priorities.  I expect 2011 to be my most productive year to date.&lt;/p&gt;&lt;div class=&quot;footnotes&quot;&gt;&lt;ol&gt;&lt;li id=&quot;fn:sociallife&quot;&gt;&lt;p&gt;See also, non-geek achievement #4, &quot;Obtained social life&quot;. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:sociallife&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id=&quot;fn:fat&quot;&gt;&lt;p&gt;See also non-geek achievement #3, &quot;Learned how to cook better&quot;. &lt;a rev=&quot;footnote&quot; href=&quot;#fnref:fat&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</description></item><item><title>Vim undo tree visualization</title><link>http://briancarper.net/blog/573/vim-undo-tree-visualization</link><guid>http://briancarper.net/blog/573/vim-undo-tree-visualization</guid><pubDate>Mon, 18 Oct 2010 15:21:16 -0700</pubDate><description>&lt;p&gt;I wrote &lt;a href=&quot;http://briancarper.net/blog/568/emacs-undo-trees&quot;&gt;previously&lt;/a&gt; about an awsome plugin to give Emacs Vim-style undo trees.&lt;/p&gt;

&lt;p&gt;Vim's undo trees are the best thing since sliced bread, but the interface for browsing through the tree is not pleasant.  The &lt;a href=&quot;http://www.dr-qubit.org/undo-tree/undo-tree.el&quot;&gt;Emacs undo-tree library&lt;/a&gt; has a way to visualize the tree and move through it with your keyboard, which solves this problem.&lt;/p&gt;

&lt;p&gt;But now, thanks to Steve Losh, &lt;a href=&quot;http://bitbucket.org/sjl/gundo.vim/src&quot;&gt;Vim has an undo-tree visualizer too&lt;/a&gt;.  Delicious.  Though it's still beta and promises to eat your babies, it seems to work pretty well.  I think the diff view of the changes for the undo is a really good idea.&lt;/p&gt;

&lt;p&gt;Thus continues the eternal Vim/Emacs arms race.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/vim/vim-undo-tree.png&quot; alt=&quot;Vim undo tree&quot; title=&quot;&quot; /&gt;&lt;/p&gt;</description></item><item><title>Productivity Booster</title><link>http://briancarper.net/blog/572/productivity-booster</link><guid>http://briancarper.net/blog/572/productivity-booster</guid><pubDate>Fri, 08 Oct 2010 15:09:40 -0700</pubDate><description>&lt;p&gt;I came up with a great way to increase my productivity recently.  You need a locally-running Apache server for this to work most effectively.&lt;/p&gt;

&lt;p&gt;First you need to set up a redirect for 404 requests to localhost.  On my system I determined that &lt;code&gt;DocumentRoot&lt;/code&gt; is &lt;code&gt;/srv/http&lt;/code&gt;, so I set this up in &lt;code&gt;/etc/httpd/conf/httpd.conf&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: xml&quot;&gt;&amp;lt;Directory &quot;/srv/http&quot;&amp;gt;
    AllowOverride All
&amp;lt;/Directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next I edited &lt;code&gt;/srv/http/.htaccess&lt;/code&gt; to redirect 404's to a main index page:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ErrorDocument 404 /index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then I created &lt;code&gt;/srv/http/index.html&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: html&quot;&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;GET BACK TO WORK, YOU HOBO&amp;lt;/title&amp;gt;
        &amp;lt;style type=&quot;text/css&quot;&amp;gt;
            html {
                background: #f00;
            }
            h1 {
                color: #0f0;
                font-family: sans-serif;
                text-align: center;
                margin-top: 100px;
                font-size: 64pt;
            }
        &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;GET BACK TO WORK, YOU HOBO&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Almost done; the final step is to edit &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;127.0.0.1 reddit.com www.reddit.com slashdot.org news.ycombinator.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;/random/get-back-to-work.png&quot;&gt;Now I see this.&lt;/a&gt;  Image hidden behind a link to spare the eyes of my readers.&lt;/p&gt;

&lt;p&gt;If this doesn't work, you could try making the background color flash quickly between red and green, or add a background MIDI and some animated GIFs.&lt;/p&gt;</description></item><item><title>iPad?  More like iAd.  Vertisements.</title><link>http://briancarper.net/blog/571/ipad--more-like-iad--vertisements</link><guid>http://briancarper.net/blog/571/ipad--more-like-iad--vertisements</guid><pubDate>Thu, 16 Sep 2010 18:43:35 -0700</pubDate><description>&lt;p&gt;Via &lt;a href=&quot;http://apple.slashdot.org/story/10/09/16/2248248/iPad-Getting-a-Subscription-Infrastructure&quot;&gt;Slashdot&lt;/a&gt;, it seems soon you may be able to subscribe to newspapers on the iPad in the near future.  &lt;/p&gt;

&lt;p&gt;Sure.  Why pay $10 for a paper copy of something when you can pay the same $10 for a likely-DRM'ed copy that can only be read on a $500 portable computer?&lt;/p&gt;

&lt;p&gt;In all honesty though, instant delivery, lack of clutter, &quot;take it anywhere&quot;, being able to archive issues indefinitely, text search... those features might be worth the money, if it was a really good newspaper/magazine.&lt;/p&gt;

&lt;p&gt;But wait, there's more.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Cupertino company has agreed to provide an opt-in function for subscribers to allow Apple to share with publishers their information, which includes vital data that news organizations use to attract advertisers, industry sources say.&lt;/p&gt;
  
  &lt;p&gt;...&lt;/p&gt;
  
  &lt;p&gt;While the leap into the digital tablet market comes with short-term problems for newspapers, the iPad and future tablets will provide a new digital palette for publications to create sophisticated and lucrative ads, said Needham &amp;amp; Co. analyst Charles Wolf.&lt;/p&gt;
  
  &lt;p&gt;&quot;I would say it's a risk, but I would argue it's a short-term risk,&quot; Wolf said. &quot;If you can put animation and multimedia into ads, that will greatly enhance reader views. I am certain of that.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So as I understand it, first I buy a $500 gadget.  Then I pay for a newspaper subscription.  Then a bunch of companies want me to give them personal information about myself, so they can share it amongst themselves.  And then I have to view ads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Animated&lt;/em&gt;&lt;/strong&gt; ads.&lt;/p&gt;

&lt;p&gt;The only thing better would be if the iPad also woke you up at 4AM and tried to sell you life insurance.  Maybe Apple would let me install a free ad-blocker script for my news reader though.  It is my hardware, after all... pfffft, yeah, I could't keep going with a straight face.&lt;/p&gt;

&lt;p&gt;And thus my desire to get an iPad, kind-of sort-of building over the past couple of months, once again flatlines.&lt;/p&gt;</description></item><item><title>Git info in your ZSH Prompt</title><link>http://briancarper.net/blog/570/git-info-in-your-zsh-prompt</link><guid>http://briancarper.net/blog/570/git-info-in-your-zsh-prompt</guid><pubDate>Fri, 10 Sep 2010 13:31:37 -0700</pubDate><description>&lt;p&gt;Recently I discovered &lt;a href=&quot;http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#SEC273&quot;&gt;vcs_info&lt;/a&gt; recently.  This nicely replaces the horrible hack I was using previously to show current Git status.  vcs_info works with VCSes besides Git, and it handles some of the magic and keeps your &lt;code&gt;.zshrc&lt;/code&gt; clean, so those are nice benefits.&lt;/p&gt;

&lt;p&gt;I used some Unicode to display colored circles.  Green if there are staged changes, yellow if there are unstaged changes, and red if there are new untracked-yet-unignored files.  Below is a picture.&lt;/p&gt;

&lt;p&gt;I like this because I'm constantly forgetting to &lt;code&gt;git add&lt;/code&gt; newly-created files.  Then I have to add them and amend my commit, and so on.  I like a prompt that reminds me that new files showed up that need to be added or ignored.&lt;/p&gt;

&lt;p&gt;Code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;brush: bash&quot;&gt;autoload -Uz vcs_info

zstyle ':vcs_info:*' stagedstr '%F{28}●'
zstyle ':vcs_info:*' unstagedstr '%F{11}●'
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:(sv[nk]|bzr):*' branchformat '%b%F{1}:%F{11}%r'
zstyle ':vcs_info:*' enable git svn
precmd () {
    if [[ -z $(git ls-files --other --exclude-standard 2&amp;gt; /dev/null) ]] {
        zstyle ':vcs_info:*' formats ' [%F{green}%b%c%u%F{blue}]'
    } else {
        zstyle ':vcs_info:*' formats ' [%F{green}%b%c%u%F{red}●%F{blue}]'
    }

    vcs_info
}

setopt prompt_subst
PROMPT='%F{blue}%n@%m %c${vcs_info_msg_0_}%F{blue} %(?/%F{blue}/%F{red})%% %{$reset_color%}'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Picture:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/random/zsh-git.png&quot; alt=&quot;ZSH and Git&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h1&gt;Limitations&lt;/h1&gt;

&lt;p&gt;As you can see in the screenshot, when you have a brand new Git repo (no commits yet), vcs_info fails to show you that there are files staged.  It works OK after you have at least one commit though.&lt;/p&gt;

&lt;p&gt;vcs_info doesn't (yet?) handle showing untracked files.  So I hacked a function to support it.&lt;/p&gt;

&lt;p&gt;Finding a good Unicode symbol that displays nicely in monospace font was annoying.  If I ever change fonts, I'll likely have to pick a new symbol.  It also doesn't display too well in a real tty.  Or over SSH when using Putty.  So I may have to scrap the stoplights and use plus-signs or something.  Sigh.&lt;/p&gt;</description></item><item><title>Vim :ruby and :rubydo scope</title><link>http://briancarper.net/blog/569/vim-ruby-and-rubydo-scope</link><guid>http://briancarper.net/blog/569/vim-ruby-and-rubydo-scope</guid><pubDate>Tue, 31 Aug 2010 10:40:51 -0700</pubDate><description>&lt;p&gt;Note to self.  In old Vim (tested in 7.2.320), I could do this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:ruby x='foo'
:rubydo $_=x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now every line in the file says &lt;code&gt;foo&lt;/code&gt;.  But in Vim 7.3 I get an error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;NameError: undefined local variable or method `x' for main:Object
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The scoping rules for Ruby in Vim must have changed somewhere along the line.  I was abusing this feature to do some handy things, so this is sad.&lt;/p&gt;

&lt;p&gt;A workaround is to use global variables in Ruby instead.  So this still works:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;:ruby $x='foo'
:rubydo $_=$x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Phew.&lt;/p&gt;</description></item><item><title>Emacs undo trees</title><link>http://briancarper.net/blog/568/emacs-undo-trees</link><guid>http://briancarper.net/blog/568/emacs-undo-trees</guid><pubDate>Tue, 17 Aug 2010 14:06:33 -0700</pubDate><description>&lt;p&gt;I've said it before: &lt;a href=&quot;/blog/361/emacs-undo-is-horrible&quot;&gt;undo in Emacs is horrible&lt;/a&gt;.  On the other hand, &lt;a href=&quot;http://stackoverflow.com/questions/1088864/how-is-vims-undo-tree-used&quot;&gt;undo in Vim is awesome&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But this is true no longer.  Now there are &lt;strong&gt;&lt;em&gt;&lt;a href=&quot;http://www.dr-qubit.org/undo-tree/undo-tree.el&quot;&gt;undo trees for Emacs!&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;  Yes, this news is so important I had to italicize and bold it.  It's like Emacs has been punching me in the face for years, and today I got it to stop.  I never thought I'd see the day.&lt;/p&gt;

&lt;p&gt;And it works great too.  You can even view the tree visually and navigate it with the cursor keys, which is a step up on what Vim offers out of the box.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/emacs/emacs-undo-trees.png&quot; alt=&quot;Emacs undo trees&quot; title=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In other news, Vim 7.3 is out and it now has &lt;a href=&quot;https://groups.google.com/group/vim_announce/browse_thread/thread/66c02efd1523554b&quot;&gt;persistent undo&lt;/a&gt; across reloads.  It's like an arms race, and gleeful hackers reap the benefits.&lt;/p&gt;</description></item><item><title>Let's draw some pixels</title><link>http://briancarper.net/blog/566/lets-draw-some-pixels</link><guid>http://briancarper.net/blog/566/lets-draw-some-pixels</guid><pubDate>Tue, 10 Aug 2010 11:19:42 -0700</pubDate><description>&lt;p&gt;I've been getting into &lt;a href=&quot;http://en.wikipedia.org/wiki/Pixel_art&quot;&gt;pixel art&lt;/a&gt; a lot lately.  It appeals to me on a lot of levels.&lt;/p&gt;

&lt;p&gt;The coder in me likes it because it's so precise.  Every pixel is placed just so.  The color palette is limited to a dozen colors.  Building a drawing out of such limited means reminds me of building programs out of primitives.  There are design patterns in pixel art: dithering, manual anti-aliasing.  There are abstractions that work and abstractions that don't.  There's a lot of goofing around with RGB values and transparency settings; it's perhaps the most deeply computer-based art form you could come up with, and as a deeply computer-based human, I really like it.&lt;/p&gt;

&lt;p&gt;The gamer in me is still partly stuck in the early 90's, so it's a huge injection of nostalgia to look at pixel art.  NES- and SNES-era games had a charm that is unmatched by anything since.  And I don't think that's &lt;em&gt;entirely&lt;/em&gt; nostalgia talking; I still play old games and they're still so much fun.  And the art in a lot of those games was just darned good.  If you stop and look at it really carefully, and start to get an understanding of how it was made, you can't help but be impressed.  &lt;/p&gt;

&lt;p&gt;The &quot;artist&quot; in me (if there is such a thing in my brain somewhere) is blown away by some of the things good pixel artists can produce.  Go look at &lt;a href=&quot;http://www.foolstown.com/&quot;&gt;foolstown.com&lt;/a&gt; and try not to slobber.  Some of this stuff just looks amazing.  Not &quot;good for a pixel drawing&quot;, but good on a level anyone could appreciate.&lt;/p&gt;

&lt;p&gt;Pixel doodles are also good practice for the RPG my wife and I are still ever-so-slowly creating.  Creating art and music for a game are turning out to be much harder work than programming it.&lt;/p&gt;

&lt;p&gt;In any case, I drew a cow standing beside a tree.  And I made a new &lt;a href=&quot;/page/565/pixel-art&quot;&gt;pixel art page&lt;/a&gt; to house my admittedly still-amateurish drawings.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/pixel/cow-tree.png&quot; alt=&quot;Cow Tree&quot; title=&quot;&quot; /&gt;&lt;/p&gt;</description></item><item><title>Review: What Do You Care What Other People Think?</title><link>http://briancarper.net/blog/564/review-what-do-you-care-what-other-people-think</link><guid>http://briancarper.net/blog/564/review-what-do-you-care-what-other-people-think</guid><pubDate>Fri, 06 Aug 2010 22:12:01 -0700</pubDate><description>&lt;p&gt;I recently reviewed &lt;em&gt;&lt;a href=&quot;http://briancarper.net/blog/559/review-surely-youre-joking-mr-feynman&quot;&gt;Surely You're Joking, Mr. Feynman!&lt;/a&gt;&lt;/em&gt;.  It was good enough that I had to get the sequel.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What Do You Care What Other People Think?&lt;/em&gt; is another collection of stories and anecdotes written by and/or about Richard Feynman.  A bit in contrast to the first book, rather than a chronological series of anecdotes, this book focuses on a couple of main topics.&lt;/p&gt;

&lt;p&gt;Feynman discusses his first wife in some detail.  Of particular interest, he describes his and his wife's brutal devotion to honesty in their relationship, even in the face of highly unpleasant truths (terminal disease, in this case).  It's the honesty of a scientist, carried into &quot;everyday&quot; life.  This was bittersweet for me to read, because the story has a sad ending.&lt;/p&gt;

&lt;p&gt;There is also a short series of letters from Feynman to others, where he discusses the silliness of pomp and circumstance, e.g. his foibles and breaches of protocol when meeting some king or other.  As someone who hates ceremony, I got a huge kick out of these.&lt;/p&gt;

&lt;p&gt;A large part of the book is devoted to discussing the &lt;a href=&quot;http://en.wikipedia.org/wiki/Feynman#Challenger_disaster&quot;&gt;Presidential Commission&lt;/a&gt; which investigated the cause of the Challenger shuttle disaster.  Feynman's full report is included in the book as well.&lt;/p&gt;

&lt;p&gt;As someone interested in astronomy and space flight (and who isn't interested in those?) I found this fascinating.  There's a lot of behind-the-scenes stuff.  Engineers are painted in a good light, managers and politicians not so much.  (Software engineers come out looking especially good, which made me feel (unjustifiably) good about myself by proxy.)  There are some diagrams and a lot of technical discussion of the shuttle.  Not so much that it drowns the narrative, but enough that I'm probably going to spend the next week reading Wikipedia on the subject now. &lt;/p&gt;

&lt;p&gt;Feynman explains his simple methods at getting to the truth in the investigation.  Go talk to the guys who put things together.  Get your hands on some O-ring rubber and test its resistance to temperature yourself in a glass of ice water.  Cut to the heart of the matter.  It's good stuff.&lt;/p&gt;

&lt;p&gt;Ultimately, as you know if you've read the report, Feynman rips NASA apart, showing that they were fooling themselves into believing the shuttle was safer than it really was.  The last sentence of the report says everything: &quot;&lt;strong&gt;&lt;em&gt;Nature cannot be fooled.&lt;/em&gt;&lt;/strong&gt;&quot;&lt;/p&gt;

&lt;p&gt;The last section of the book discusses the value of science.  More specifically, Feynman discusses the value of doubt.  I very much liked how the chapter ends:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is our responsibility as scientists , knowing the great progress which comes from a satisfactory philosophy of ignorance, the great progress which is the fruit of freedom of thought, to proclaim the value of this freedom; to teach how doubt is not to be feared but welcomed and discussed; and to demand this freedom as our duty to all coming generations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If there's one trait I had to pick to separate good people from bad, it would be the ability to admit being wrong.  And if I had to separate the good from the excellent, it would be not just the ability to admit being wrong, but the eagerness to be proved wrong.&lt;/p&gt;

&lt;p&gt;There's a certain kind of devotion to the truth that not many people achieve, and maybe not many people even want to achieve.  There's comfort in thinking that you &lt;em&gt;know&lt;/em&gt; things.  It's very tempting.  I think it's probably partly why most people are religious.  I suspect it's a big reason why so many people are so stubbornly wrong about so many things in general.  I suspect this comfort is an enormous source of suffering in the world.&lt;/p&gt;

&lt;p&gt;But there's another kind of comfort that people miss out on.  It's the comfort of knowing that although you're probably wrong about a lot of things, you're trying your hardest to be right.  You pay the price of being aware of your own state of ignorance, but you can rest a bit easier knowing that you're maybe, hopefully, inching towards the truth.  I never heard the word &quot;freedom&quot; used to describe this feeling before, as Feynman does above, but it fits.&lt;/p&gt;

&lt;p&gt;That's why I like reading about Feynman and reading Feynman's words.  He seemed to live this philosophy as well as anyone could hope to.&lt;/p&gt;</description></item><item><title>X automation with xte</title><link>http://briancarper.net/blog/563/x-automation-with-xte</link><guid>http://briancarper.net/blog/563/x-automation-with-xte</guid><pubDate>Fri, 06 Aug 2010 13:03:01 -0700</pubDate><description>&lt;p&gt;I learned today (via a &lt;a href=&quot;http://hanschen.org/2009/10/13/mouse-shortcuts-with-xbindkeys/&quot;&gt;great blog post&lt;/a&gt;) about &lt;code&gt;xte&lt;/code&gt;.  This program lets you simulate X Windows mouse and keyboard events from the commandline.  How much more awesome can you get?  &lt;/p&gt;

&lt;p&gt;Hans illustrates how to integrate &lt;code&gt;xbindkeys&lt;/code&gt; and &lt;code&gt;xte&lt;/code&gt; to make KDE4 effects activate.  I wanted the KDE4 &quot;Desktop Grid&quot; to appear when I press a mouse button (because my &lt;a href=&quot;http://briancarper.net/blog/558/review-logitech-performance-mx&quot;&gt;new mouse&lt;/a&gt; has a lot of buttons to spare), so this is exactly what I was looking for.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xte&lt;/code&gt; is the kind of glue that makes Linux awesome.  KDE lets you set global keyboard shortcuts for lots of things.  &lt;code&gt;xbindkeys&lt;/code&gt; lets you assign shell commands to mouse buttons.  And &lt;code&gt;xte&lt;/code&gt; ties the two together.  Possibly none of the programmers on these three tools knew about the others, but they interact perfectly to let you do anything you want.&lt;/p&gt;

&lt;p&gt;You may be thinking, &quot;&lt;em&gt;If you want to work with KDE from the commandline, why not use DBUS?&lt;/em&gt;&quot;  That's what I tried to do first.  But I can't for the life of me figure it out.  There's &lt;a href=&quot;http://osdir.com/ml/kde-devel/2010-04/msg00195.html&quot;&gt;some indication&lt;/a&gt; that we might be able to do this somday, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;qdbus org.kde.kglobalaccel /component/kwin org.kde.kglobalaccel.Component.invokeShortcut ShowDesktopGrid
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or maybe it's already in the latest version of KDE and I haven't upgraded yet.  Either way.&lt;/p&gt;

&lt;p&gt;By the way: could DBUS possibly have a more verbose or cryptic interface?  I was hunting through the available DBUS commands looking for something that would show the Desktop Grid, and I ended up having to scan through lists of crap like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;~ % qdbus org.kde.kwin /KWin                         
method Q_NOREPLY void org.kde.KWin.cascadeDesktop()
method void org.kde.KWin.circulateDesktopApplications()
method bool org.kde.KWin.compositingActive()
signal void org.kde.KWin.compositingToggled(bool active)
method int org.kde.KWin.currentDesktop()
method QList&amp;lt;int&amp;gt; org.kde.KWin.decorationSupportedColors()
method void org.kde.KWin.doNotManage(QString name)
method Q_NOREPLY void org.kde.KWin.killWindow()
method QStringList org.kde.KWin.listOfEffects()
method void org.kde.KWin.loadEffect(QString name)
method QStringList org.kde.KWin.loadedEffects()
method void org.kde.KWin.nextDesktop()
method void org.kde.KWin.previousDesktop()
method Q_NOREPLY void org.kde.KWin.reconfigure()
method void org.kde.KWin.reconfigureEffect(QString name)
method void org.kde.KWin.refresh()
signal void org.kde.KWin.reinitCompositing()
signal void org.kde.KWin.reloadConfig()
method bool org.kde.KWin.setCurrentDesktop(int desktop)
method void org.kde.KWin.showWindowMenuAt(qlonglong winId, int x, int y)
method Q_NOREPLY void org.kde.KWin.toggleCompositing()
method void org.kde.KWin.toggleEffect(QString name)
method Q_NOREPLY void org.kde.KWin.unclutterDesktop()
method void org.kde.KWin.unloadEffect(QString name)
method bool org.kde.KWin.waitForCompositingSetup()
method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface_name, QString property_name)
method QVariantMap org.freedesktop.DBus.Properties.GetAll(QString interface_name)
method void org.freedesktop.DBus.Properties.Set(QString interface_name, QString property_name, QDBusVariant value)
method QString org.freedesktop.DBus.Introspectable.Introspect()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is line noise to me.&lt;/p&gt;</description></item><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></channel></rss>

