Sometimes one comments parts of code. Eg. to quickly check a different variant of the code. Or to return a mock value while debugging. Clojure provides several ways to comment code. Some more obvious than others...
The obvious way to add a comment is to use ;
. It introduces a line
comment, that is the rest of the source code line is ignored. This is
nice to add „usual“ comments to source code. But temporarily commenting
code can be tedious.
(if (some-test)
; (production-code a b c)
(temporary variant)
(else-branch))
In this case it was easy. Just comment the whole line and everything
is fine. However what happens if we just want to comment b
and
replace it with a different value? We have to break the line (or c
and the closing paren will be ignored) or we have to copy the whole line
and take the above approach.
(if (some-test)
; (production-code a b c)
(production-code a mock c)
(else-branch))
Both approaches are not very compelling and involve a lot of ceremony to get the code correct again.
So ;
is – let's say – suboptimal to comment code temporarily.
Another way to comment code is the comment
macro. However, this is
even less compelling than ;
. There are some subtle issues. Who can
spot the bug (without entering things into a repl)?
(if (some-test)
(comment (production-code a b c))
(temporary variant)
(else-branch))
ticktackticktack beep As I said: comment
is a macro. Hence, it
has to evaluate to something. In case of comment
this something is
nil
. So we have actually:
(if (some-test)
nil
(temporary variant)
(else-branch))
if
might complain. Other forms might not and happily go on with
now broken code (eg. cond
). For similar reasons, we cannot easily
mock out b
: it is replaced by nil
changing the function arguments.
Furthermore the contents of the macro must be valid code. In our
case this is true. But that means that comment
cannot be used for
arbitrary comments as with ;
.
(All in all, I don't see a reason, why one would ever want to use
comment
besides wanting to comment several toplevel forms. But then
I can use ;
just as well.)
So commenting out code for testing purposes seems to be pretty tedious
in Clojure. But wait! The reader to the rescue: #_
.
The reader features a reader macro, which drops the next thing read.
It is not replaced with nil
or whatever. It just reads the form from
the input and drops it under the table. The compiler actually never
sees the form. So we can write our initial example as:
(if (some-test)
#_ (production-code a b c)
(temporary variant)
(else-branch))
In contrast to the comment
case this works as intended and we don't
have to fiddle with the parens. Similar, we can easily mock out b
.
(if (some-test)
(production-code a mock #_ b c)
(else-branch))
We simply add our mock and ignore b
. And it doesn't matter whether
b
is a symbol, a string or some arbitrarily complicated subform!
(if (some-test)
(production-code a
mock
#_ (map (fn [x]
(do-complicated-stuff x)
(with even-more stuff))
(filter some-predicate? xs))
c)
(else-branch))
But again: the thing following #_
must be valid code. So no arbitrary
comments.
Beware the leopard! #_
gives also an easy way to shoot one's foot!
(if (some-test)
(production-code a #_ b c)
(else-branch))
Now c
is suddenly in the position of b
.
There are several ways to „comment“ code. ;
is the most straightforward
and behaves as you would expect from other languages. #_
and comment
however have some subtle gotchas, which one has to be aware of.
Nevertheless, at least #_
is quite useful and should belong into
one's toolbox.
Published by Meikel Brandmeyer on .
I'm a long-time Clojure user and the developer of several open source projects mostly involving Clojure. I try to actively contribute to the Clojure community.
My most active projects are at the moment VimClojure, Clojuresque and ClojureCheck.
Copyright © 2009-2014 All Right Reserved. Meikel Brandmeyer