/ CLOJURE, FUNCTIONAL PROGRAMMING

Decoding Clojure code, getting your feet wet

Recently, I had some some fun writing functional Kotlin to solve the FizzBuzz test. I asked for some feedback, and one of the answer I received was in Clojure:

The code on the left side is the following:

(defn div-by? [n d]
  (zero? (mod n d)))

(defn fizz-buzz [n]
  (condp = [(div-by? n 3) (div-by? n 5)]
         [true true] "FizzBuzz"
         [true false] "Fizz"
         [false true] "Buzz"
         (str n)))

(->> (range 1 100)
     (map (comp println fizz-buzz)))

I don’t know anything about Clojure, but only it’s a functional language. I think it’s an interesting exercise to try to make sense of the above snippet, especially since it’s quite limited in size. In this post, I’ll adopt the perspective of a Java developer.

Parentheses, parentheses everywhere

If you’re familiar with the family of C languages only (like myself), the snippet looks quite obscure. The first thing to start with is to know that Clojure belongs to the Lisp family of languages. The latter enforce parentheses around every expression, statement, call, etc.:

(str n) ; returns n.toString()

No static type checking

Clojure has no static type checking. None. Zero. Zilch. You can check the absence of types in the solution above.

If one is really uncomfortable about that, there’s a subproject bringing types to Clojure.

No return keyword

Clojure is a functional language. As such, it does its best to enforce the writing of functions:

A function is a process or a relation that associates each element x of a set X, the domain of the function, to a single element y of another set Y.
— Wikipedia
https://en.wikipedia.org/wiki/Function_(mathematics)

That means every function is supposed to return. Hence, the return keyword is implicit, as seen from the above snippet.

Functions that are not supposed to return a value - impure functions e.g. println return nil anyway.

Prefix notation

Clojure requires writing first the method name, and then arguments (if required). This is pretty similar to Java, but Clojure applies that pattern everywhere, even for arithmetics:

(mod n d) ; returns the reminder of n divided by d

Coding conventions

  • Clojure identifiers use hyphens, instead of using camel case as would be in Java
    (div-by? n 3) ; calls the div-by? function with arguments n and 3
  • Function returning a boolean value should end with ?

Defining functions

The defn macro allows to define a new function. Arguments of the macro are:

  1. The function name
  2. The list of arguments, specified as an optionally-empty array
  3. The function body
(defn
  div-by?            (1)
  [n d]              (2)
  (zero? (mod n d))) (3)
1 Function name
2 Function arguments, wrapped in an array
3 Function body

Chaining functions

Function chaining is achieved through the comp macro:

(def times-inc (comp inc *)) (1)
(times-inc 2 3)              (2)
1 Composes functions of multiply and increment by one
2 Calls the defined function with arguments 2 and 3, returning 7

Switch

The condp macro replaces the traditional switch statement, but is more powerful. It’s more similar to the pattern matching feature found in Kotlin and Scala.

(condp = [(div-by? n 3) (div-by? n 5)] ; a two-elements array of boolean
  [true true] "FizzBuzz"
  [true false] "Fizz"
  [false true] "Buzz"
  (str n)) ; if no other match, this is the default

Summary

To sum up, here are Java equivalents of the above code snippets (or Kotlin when Java is not enough):

Clojure Java/Kotlin
(str n)
return n.toString();
(mod n d)
return n % d;
(div-by? n 3)
return divBy(n, 3);
(defn div-by? [n d]
  (zero? (mod n d)))
boolean divBy(int n, int d) {
  return n % d == 0;
}
(def times-inc
  (comp inc *))
(times-inc 2 3)
UnaryOperator<Integer> inc = i -> i + 1;
BinaryOperator<Integer> times = (i, j) -> i * j;
BiFunction<Integer, Integer, Integer> timesInc =
  times.andThen(inc);
return timesInc.apply(2, 3);
(condp = [(div-by? n 3)
          (div-by? n 5)]
  [true true] "FizzBuzz"
  [true false] "Fizz"
  [false true] "Buzz"
  (str n))
return when (divBy(i, 3) to divBy(i, 5)) {
  true to true -> "FizzBuzz"
  true to false -> "Fizz"
  false to true -> "Buzz"
  else -> i.toString()
}

TODO

There are still some subtle points that I need to dig deeper in:

  • The difference between a macro and a function
  • The →> macro
  • The difference between def and defn

Afterwards, potential next steps include:

  1. Decode the code displayed on the right part of the original tweet
  2. Dive into collections
  3. Learn about Clojure/Java interoperability
  4. Try to develop a basic Spring Boot application
  5. Check what are the equivalents of Java frameworks for a "classical" stack e.g. web development, persistence, logging, build, etc.
Nicolas Fränkel

Nicolas Fränkel

Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.

Read More
Decoding Clojure code, getting your feet wet
Share this