PurelyFunctional.tv Newsletter 352: Tip: use the right kind of comment for the job

Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.

Issue 352 - November 18, 2019 ยท Archives ยท Subscribe

First Annual PurelyFunctional.tv Survey! ๐Ÿ“‹

I'm happy to announce the first annual PurelyFunctional.tv Survey. Your answer to this quick survey will help me understand how to improve my videos and help you master Clojure faster and more deeply.

There are only four questions. If you've watched any of my video content, please take a few minutes to fill this out. I appreciate any answer you can give.

Fill out the survey

The survey will run for a few weeks.

Clojure Tip ๐Ÿ’ก

use the right comment for the job

Let's face it: sometimes commenting out code is useful. Sometimes you want to test a large piece of code without launching the missile just yet. Comment out the code! And many Clojurists keep snippets of code in a comment at the end of a file to facilitate Repl-Driven Development. And then, of course, there are comments that are actually comments on the code.

Clojure has three ways to ignore input. Each is different in significant ways. Each has found a use in the community. Let's go over each of them.

Line comments

If you put a semicolon in your code (anywhere outside of a string or a character literal) it starts a comment that goes till the end of the line. Anything characters are allowed in that comment. It won't be read or parsed, just ignored. People use these for two purposes:

1. To add a note in natural language to the code.

;; 1002 works. I don't know why. Change at your own risk.
(run-me 1002) ; here is the magic number

As a community, and in many editors, double semicolons indicate comments that shouldn't be grossly indented. Single semicolons are for comments that come after other code on the same line.

2. To comment out a single line of code.

Sometimes the code you want to comment out is all on one line at the end of the line, and you just want to insert a single ;. Go for it.

(doseq [task tasks]
  ;;(println task)
  (run-task task))

comment macro

There's a macro called comment that will comment out any number of forms. All you have to do is surround the forms with the comment macro. There are some gotchas:

  1. It's a macro, so the forms inside have to be valid for the Clojure reader. That means you have to obey all the syntax rules for numbers, symbols, and braces matching.
  2. The comment form itself is still an expression in your program. It evaluates to nil. That could have an effect on any surrounding forms.

In short, it's just a way to avoid executing code, but not useful for text. It's also not very useful for commenting out sub-expressions since it evaluates to nil.

So what do people use this for? Mostly for code they want to run during development but not in the normal flow of production code. It's common to put a comment block at the end of a file with expressions to evaluate for testing.

It sounds weird, but it's better than typing in the same expressions over and over into the REPL prompt. Most editors have a setting so you can evaluate expressions inside the comment just like top-level expressions.

#_ reader macro

Clojure's reader has a reader macro (different from a regular macro) that directs the reader to ignore the next form. That is, it reads a form and then throws it away. It's a nice way to ignore an entire expression.

#_(println a

Notice that this one works really well for multi-line expressions. Unlike ;, which ignores everything up to the end of the line, #_ ignores the entire next expression. It's the perfect way to "turn off" one expression. It even works for sub-expressions---it is truly ignored by the reader, not replaced with nil. It's great for dropping an argument, for instance.

But get this: they stack. Often, we want to ignore two expressions, not just one. Here's a case:

(def mappings {:a 1 :b 2 :c 3})

What if we want to get rid of the mapping of :a to 1? We could do this:

(def mappings {#_:a #_1 :b 2 :c 3})

But because #_ ignores the next expression, there is a better way. Two #_s will ignore the next 2 expressions:

(def mappings {#_#_:a 1 :b 2 :c 3})

Now there's just one thing to delete if you want to turn it back on.

Thanks to Ray McDermott and the rest of the panel at Apropos Clojure for this final tip.

Conference alert ๐Ÿšจ

I booked my tickets to the 2019 Clojure/conj in Durham, North Carolina. If you're going, let me know! I haven't been to a Conj in a couple of years and I'm really excited.

Ask me about my book and I'll give you a discount code.

I am considering bringing a board game to play at the board game night.

Currently recording ๐ŸŽฅ

Property-Based Testing with test.check is now split into three courses, Beginning, Intermediate, and Advanced.

Last week I promised a flood but all I've got is a trickle. Coming back from vacation is hard :). However, they are now available to purchase for a low price for the launch sale. I will end the sale after the Conj. That gives you two weeks to buy them. They will never be this inexpensive again!

Book update ๐Ÿ“–

I have been getting such great stories from readers of Grokking Simplicity. People tell me they are making their code more organized and testable. After all of the work I put into the book, it feels good that it's having such a great impact.

You can buy the book and use the coupon code TSSIMPLICITY for 50% off.

Thanks for reading.

Clojure Challenge ๐Ÿค”

Last week's challenge

Well, I got no submissions from last week.

This week's challenge

Write a program that outputs all possibilities to put a + or - or nothing between the numbers 1-9 (in order) such that the result is 100. For example:

1 + 2 + 34 - 5 + 67 - 8 + 9 = 100

The function should output a list of strings with these formulas.

Hint: this is a recursive problem. Use divide and conquer.

As usual, please reply to this email to send me your submissions. I'll collect them up and share them in the next issue. If you don't want me to share your submission, let me know.

Rock on!
Eric Normand