Parenthesical Culture and ParEdit

After a few years of doing zero Clojure hacking, recently I've picked up a side project in Clojure again. Rather than using my usual editors to edit Clojure code, I chose to go the all-in hardcore Lisp route: Emacs with Paredit (I followed these instructions to set it up). And during that time I developed a theory of why people feel like they drown in the parentheses in Lisps and why the Lisp community fails to be bothered by this issue. Hint: ParEdit.

I know, Emacs: ancient tool, who uses that anymore? As it turns out, in fact, quite a few people do. Granted, primarily people that did programming before Textmate and Sublime existed. Unsurprisingly, Emacs' Lisp support is state-of-the art -- possibly this has something to do with the fact that the whole thing was written in its own Lisp dialect named Elisp.

While everybody heard of Emacs, you may not have heard of ParEdit yet. The first time you enter it, you think you've gone bananas: basic things no longer work. For instance, you cannot delete parentheses most of the time. What's up with that?

The goal of ParEdit is to make it easier to keep your parentheses balanced, whether they're of the round, square or squigly kind. It does so, and many other editors do this, by immediately inserting a closing parenthesis whenever you open one. However, it goes further than that, by disallowing you to remove non-empty parenthesis pairs. Instead, it offers way to do structural editing of your parentheses referred to as slurping and barfing. Here's a little YouTube feature that show this in action (I suggest you fast forward to the 1 minute mark, to skip over the rather toe curling intro):

Beside offering keyboard shortcuts to manipulate your parenthesized lists (named s-expressions), there are also shortcuts to navigate them, for instance to jump to the next branch of the current expression, taking nesting into account.

Once you go passed the initial tearing-your-hair out phase of using ParEdit, you start to appreciate it and appreciate that it's in fact a simple form of structural editing: you are no longer editing plain text, you are editing code structurally as trees. An idea recently reinvented by JetBrain's MPS

So, let me get to my observation about parenthesis use in Lisps. Or Lisp's parenthesical culture, if you will.

Parentheses are not unique to Lisp. In fact, a Lisp like Clojure barely uses more parentheses than for instance a Java, or JavaScript does. So where does the complaining come from? I think there's two sources: the first is that traditional Lisps (like common lisp and Scheme) do use more parentheses, and they use they use the same type of parenthesis for about everything: they overload the meaning of the parenthesis. That's indeed bad and was fixed in Clojure. The second is about parenthesis placement.

Let me illustrate with an example: Here's a completely non-sensical JavaScript fragment:

function doStuff() {
   while(true) {
      var result1 = callSomeFunction();
      var result2 = callOtherFunction(result1);
      if(result2 === null) {
         console.log("Got null");
      }
   }
}

The Clojure equivalent to this would be the following:

(defn do-stuff []
   (while true
      (let [result1 (call-some-function)
            result2 (call-other-function result1)]
         (when (= result2 nil)
            (println "Got null")))))

The scary part about the Clojure code is the end there: ))))). That's five, yes five parentheses. OMG! Lisp, that's so typical of you!

The thing is, you may also notice that the Clojure code is a few lines shorter: 3 to be exact. How come? Well, the last three lines of the JavaScript are all spent closing brackets. We can shorten it, though -- no worries:

function doStuff() {
   while(true) {
      var result1 = callSomeFunction();
      var result2 = callOtherFunction(result1);
      if(result2 === null) {
         console.log("Got null");}}}

Tada! Same length. But oh no! 4 parentheses at the end (note that it's 4 not five because Clojure's let scoping mechanism introducing 1 extra one, yes Clojure has lexical scopes -- insanity). Four! Clearly this proofs that JavaScript is just a Java-like syntax on Scheme, right?!

However, similarly, we could also write our Clojure code this way:

(defn do-stuff []
   (while true
      (let [result1 (call-some-function)
            result2 (call-other-function result1)]
         (when (= result2 nil)
            (println "Got null")
         )
      )
   )
)

How do you like them apples?

My guess is that the latter code fragment looks slightly less frightening to the non-trained Lisper's eye than the first version. It's also more practical, it's much easier to add a new statement right after the when, because you don't have to count which parenthesis to insert stuff in front of.

So why don't Lispers write their code this way? My theory is that there are two reasons:

  1. Having lines with just parentheses on them is a waste of screen space (same is true for other languages, but that hasn't bothered anybody, apparently).
  2. You don't have to count parentheses with a tool like ParEdit that much.

So, how would ParEdit help here? Here's what I'd do in Emacs' ParEdit mode if I'd like to add something just behind the when. First I'd position my cursor somewhere before the when, for instance like this:

(defn do-stuff []
   (while true
      (let [result1 (call-some-function)
            result2 (call-other-function result2)]
  |      (when (= result2 nil)
            (println "Got null")))))

Next, I'd press Ctrl-Meta-f to move to the next branch at the same level, which moves the cursor here:

(defn do-stuff []
   (while true
      (let [result1 (call-some-function)
            result2 (call-other-function result1)]
        (when (= result2 nil)
            (println "Got null"))|)))

Then I press return, and voila:

(defn do-stuff []
   (while true
      (let [result1 (call-some-function)
            result2 (call-other-function result1)]
        (when (= result2 nil)
            (println "Got null"))
        |)))

Tools like ParEdit really change the way you manipulate your (Lisp) code.

If you write Clojure, some other Lisp or sometimes dabble with them, be sure to give Paredit a try, there are implementations for other editors as well, for instance for Vim and Sublime.