Rich Comment Blocks
The three main types of comments in Clojure are
- comment
- discard comment
- comment macro (
Rich Comment
)
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 a comment
is to add human readable documentation to your
code to help the people reading our code understand it.
We can also comment out blocks of code which we don't want our program
to run.
Discard Comment
The second type of comment is a discard comment
(-> 5 inc inc inc) ; 8
(-> 5 inc #_ inc inc) ; 7
The #_
will comment out the form directly behind it. In the above, the code
commented out is the second inc
.
I actually ignored this tool for an embarasignly long time, but it's wildly useful and actually makes coding faster!
At a high level, what makes the discard
comment great is that it doesn't
return a value, they nest and you can insert it into the middle of a line of
Clojure code and it only comments the form directly behind it.
Additionally, if you're commenting out an entire block of code you only have
to add the discard
to the top level of the form and the entire form will be
commented. This means the formatting of the code block won't be affected
and you have very little code to remove.
Here's an example of nesting:
(-> 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"})
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
(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.