Did you know about comments?

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

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.

The `comment` macro

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.)

The Reader to the Rescue

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.

Upshot

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 .