Clojure Conventions
Explore the key conventions in Clojure coding that support writing clean, maintainable, and extensible programs. Understand proper naming patterns, spacing, indentation, line usage, and namespace management to ensure your code adheres to functional programming standards and best practices.
Like most languages, Clojure has conventions and best practices for the layout and organization of source code. Let’s take a look at these below.
Names
As you might have noticed, Clojure uses the kebab-case to define most of the names in Clojure, which means that the names are all in lowercase and separated by the dash symbol (-). We’ll define def, defn, bindings in general, and many others with this style.
Use PascalCase for protocols, records, structs, and types.
The names of predicate functions, which return a boolean value, should end in a question mark (?). In this way, we always know that that function is a predicate. A Clojure example is the even? function.
The names of functions that might generate a side effect should end with an exclamation point (!). Examples of these are functions that interact with databases, call an HTTP request, or do anything else that’s considered a side effect. A Clojure example is the reset! function.
Use -> instead of to in the names of conversion functions.
Spaces
Use spaces for indentation instead of hard tabs, which use the tab character.
Use two spaces to indent internal forms that have body parameters, like def, defn, special forms, and macros let, when-let, when, cond, as->, cond->, case, with-*, etc.
Function arguments should be vertically aligned when spanning multiple lines.
Vertically align let bindings and hashMap keywords.
There should be no spaces between brackets, but outside the brackets, there should be a space before more content is added.
Lines
The position of the argument when defining a function with defn can be at the same line or in the line below.
The only exception to same-line implementation and arguments goes to the defmethod function.
All trailing parentheses should be put in a single line instead of distinct lines, as we sometimes see in other languages.
Commas
Don’t use commas between the elements of sequential collection literals.
Namespaces
The namespace of a file should consist of the whole path from the source to the file name.
When referring to other namespaces, keep a consistent naming to the alias. Don’t use different aliases for the same namespaces within a service. As a general first rule, make the alias the same as the namespace name with the leading parts removed.
In the namespace, it’s preferable to use :require with namespaces with the :as alias instead of using :refer to a specific function. Using :refer can lead to confusion while reading of the code because it will look like the function belongs to the same namespace. Also, avoid using :require :refer :all, which is more problematic than only referring to specific functions. In this case, we won’t be able to determine where the function originates from.
Syntax
Don’t define variables inside functions; use let instead.
Use when instead of (if ... (do ...).
Use if-let instead of let + if if we have a single variable. The same principle applies to the other variations of conditionals, when-let, if-not, when-not, and so on.
Don’t wrap functions in anonymous functions if it’s not needed, especially in transformation functions, such as filter, map, reduce, and similar functions that can directly accept anonymous functions.
It’s preferable to use threading macros, thread-first (->) and thread-last (->>), to avoid excessive nesting of forms.
Keeping these points in mind, we’ll be able to develop and validate if a Clojure code is well written or not.