Rich Comment Blocks

The three main types of comments in Clojure are

The last one, the Rich Comment, is a pretty cool feature of Clojure.

Comment

The first type of comment is a literal comment

; I'm a comment

Anything that follows the ; is ignored by Clojure until the end of the line. A common use for the comment is to help future humans understand our code better.

Discard Comment

The second type of comment is a discard comment

(-> 5 inc inc inc)     ; 8

(-> 5 inc #_ inc inc)  ; 7

In the above code, the #_ makes it as if the second inc doesn't exist. This is great for debugging because the discard comment doesn't return a value. In addition to this, there are two additional usage notes about the discard comment.

The first is that they nest:

(-> 5 #_ inc #_ inc inc) ; you could discard each form in turn
(-> 5 #_ #_ inc inc inc) ; or you could stack them

Here are some examples of where you might find the discard comment useful:

(or #_ (int? 2) (nil? "Thomas"))

(let [my-number 5
      #_ #_ another-number 13]
  ;...
  )

{#_ #_ :name "Between Two Parens" :host "Thomas"}

The second take away is that you don't have to add spaces after the discard comment:

;; these all produce the same result
(-> 5 #_ inc #_ inc inc) ; space
(-> 5 #_inc #_inc inc)   ; no space

(-> 5 #_ #_ inc inc inc) ; space
(-> 5 #_#_inc inc inc)   ; no space

The difference between adding the space or removing the space is which one you find more readable.

Rich Comment

Finally, we have the comment macro which is more affectionatley known as a Rich Comment Block:

(comment
  ; everything in here is ignored...returns nil
  )

The first time I heard of a Rich Comment was in Stuart Halloway's excellent talk Running With Scissors where he notes:

Yet, even after watching Running With Scissors the use of the Rich Comment hadn't started to click yet. Two more things would need to happen: The first, I would witness REPL Driven Development used in person by David Nolen. The second, I would start to use REPL Driven Development in my own workflow. When I did these things, I was able to better see the benefits of the comment macro as

  • documentation
  • a save point
  • code setup
  • improved code exploration
  • preservation of syntax highlighting

With that, let's review a few examples of the Rich Comment from real life Clojure codebases.

The first example illusrates the documentation and save point ideas.

(comment

(println (sh "ls" "-l"))
(println (sh "ls" "-l" "/no-such-thing"))
(println (sh "sed" "s/[aeiou]/oo/g" :in "hello there\n"))
(println (sh "sed" "s/[aeiou]/oo/g" :in (java.io.StringReader. "hello there\n")))
; ...
)

The above comes from the clojure codebase itself and is a code example of how to use sh. For me, the value is that we have an example of how to use sh (documentation) and we have some code ready for us to run through our REPL (a save point). This idea of having a save point becomes more powerful in the next example:

(comment
  (do
   (require '[my.app.db :as app.db])
   (require '[my.app.cart :as cart])
   (def db (app.db/connection!)))

  (cart/add db {:item-name "iPhone"})
  ; ...more stuffs
  )

The above builds on the idea of having a save point and layers on some code setup helpers. What the above does is add in a few lines of code which, when run, will provide us with a db connection. Through this, I can quickly begin interacting with my app's database code and building out features.

Of course, there are other types of setup code that you may want. For example, you might be working on a pure function which is just going to transform some data. In this case, we might setup a comment like this:

(comment
(refer 'set)
(def xs #{{:a 11 :b 1 :c 1 :d 4}
         {:a 2 :b 12 :c 2 :d 6}
         {:a 3 :b 3 :c 3 :d 8 :f 42}})

(def ys #{{:a 11 :b 11 :c 11 :e 5}
         {:a 12 :b 11 :c 12 :e 3}
         {:a 3 :b 3 :c 3 :e 7 }})

(join xs ys)
; ...
)

The above is the example Stuart provided in his talk which provides us with some sample data allowing us to immediately begin using our functions to transform said data.

Conclusion

These are just a few examples of how to use a Rich Comment Block. The most interesting part of the Rich Comment Block for me is that it's a tangible example of the pragmatism of Clojure. In this case, the comment macro provides an additional mechanism for speeding up my workflow and making our code more maintainable overall because of the improved documentation and context we get from these comments.