Inverting 'If' Statements in Clojure

How about a little code golf?

September 2, 2021

Consider the following if/else statement, which is valid Go syntax:

func main() {
	n := rand.Intn(50)
	if 10 >= n && n > 20 {
		fmt.Println("n is between 10 and 19")
	} else {
		fmt.Println("n is out of bounds")
	}
}

Let's suppose you wanted to invert the if statement and reverse the ordering of the fmt.Println calls. What would the steps be? What would the refactored code look like?

As for the steps, well, in order to invert everything about that conditional logic you might do something like this:

  1. Change the logical 'and' to an 'or': && -> ||
  2. Reverse the directions of the greater-than symbols: > -> <
  3. Move the equals sign from the first operator to the last. (I'm willing to bet you forot this step, or wondered if it was correct...)

Here's the finished refactoring:

func main() {
	n := rand.Intn(50)
	if 10 < n || n <= 20 {
		fmt.Println("n is out of bounds")
	} else {
		fmt.Println("n is between 10 and 19")
	}
}

Well, it seems like only yesterday that GoLand (the Go IDE by Jetbrains) released with the capability to invert if/else statements such as this one by executing a simple <ALT>+<ENTER> keystroke, which takes the guess-work out of such manual refactoring. (Even so, you would, of course, have a thorough test suite to prevent the introduction of a bug...)

Let's consider this same task but with Clojure code:

(if (and (>= 10 n) (> n 20))
  (println "n is between 10 and 19")
  (println "n is out of bounds"))

I recently wanted to invert such an if expression and I was initially sad that Cursive, (the clojure IDE built on top of JetBrains Intellij) didn't sport GoLand's 'if-inversion' capability. So, thinking I was clever I simply wrapped the conditional express in a (not):

(if (not (and (>= 10 n) (> n 20)))
  (println "n is out of bounds")
  (println "n is between 10 and 19"))

Of course, this same approach is possible in the Go code:

if !(10 >= n && n > 20) ...

Then I learned about the existence of Clojure's if-not form, which, in just four keystrokes, elegantly inverts what was an if expression and saves a set of parenthesis in the process!

(if-not (and (>= 10 n) (> n 20))
  (println "n is out of bounds")
  (println "n is between 10 and 19"))

There are few additional pairs of complementary functions: