Clean Clojure: Meaningful Names
I came to 8th Light for a chance to write Clojure, and in the last few weeks, the lofty dream of slinging s-expressions during my day job has finally come true. (Apprentices are compensated at a flat rate of $0.002 per parenthesis). After spending three months learning rules and practices for writing clean object-oriented code, I’m now mapping them to functional programming. Over the next few posts, I’ll try translating some of the guidelines for clean code to Clojure. Like any style guide, there will be room for opinion, so don’t hesitate to leave comments or offer suggestions.
Clean Code is worth the cover price for Chapter 2 alone. Its advice is simple: use meaningful, clear names that reveal intent. This rule probably seems obvious, but the value is in its side effects. Taking the time to scrutinize every name requires the sort of mindfulness and thought that produces clean code. In addition to Uncle Bob’s general guidelines for good names, here are a few Clojure-specific rules on naming.
Verbs rule
Clojure’s categorical imperative: act in the Kingdom of
Verbs.
Functions do things, and their names should describe the things they
do. This is usually an easy rule to follow, but functions that build
or return data structures can be tricky. Make-user-map
is better
than user-data
. Render-footer
is better than footer
alone.
But nouns are useful
Verbs are great, but they’re even greater when they have objects. A name like
remove-temporary-files
is much clearer than clean-up
.
Nouns are also useful inside functions. I find my tolerance for
repetition far lower in Clojure than in other languages: if I use an expression more
than once, I’ll almost always put it in a let
binding and give it a
name. Inside functions that compose multiple transformations on some
data structure, extracting intermediate steps into values in a let
binding can be very helpful.
1 2 3 4 5 |
|
Good nouns are also helpful when destructuring
values, which is awesomely useful but sometimes hard to read. Prefer
putting them in let
bindings to cramming them in the argument list,
except for very simple cases.
1 2 3 4 5 6 7 8 9 |
|
And okay fine, also adjectives
The one first-class exception to verbs everywhere is adjectives for
predicates (functions that return true
or false
, like odd?
and
seq?
). These should always end in question marks and always return
either true
or false
.
1 2 |
|
Use the best name…
Clojure has a large set of core functions, and sometimes the clearest name for a function will collide with one of them. Use it anyways! This is why namespaces are useful. Similarly, don’t worry if the best name is a long one–it’s easy to rebind it to a new name when required.
…But don’t mislead
That said, make sure it really is the best name. Long names often
indicate functions that can be split: invert-and-multiply
and
find-and-replace
should probably be split in two. (Hint: and
is a
great clue). If a function’s name collides with a core function or
incorporates a common name, it should act the same way: if table-map
doesn’t apply a function to every cell in a table, it has the wrong name.
Use idiomatic vocabulary
The Clojure style guide, Clojuredocs examples and Clojure’s own library coding standards are good resources for picking up common Clojure idioms and vocabulary. Here are a few naming conventions.
In macros, expr
is usually used for a single expression and body
for a longer form.
1 2 |
|
“Collection” is often shortened to coll
:
1 2 |
|
Bundling up extra arguments is almost always done with & more
.
1 2 3 |
|
Like in middle school math, n
is usually an integer, x
and y
are
default numerical inputs, and f
, g
, and h
are often functions.
1 2 3 4 |
|
Dynamic vars wear *earmuffs*
. Try not to use them.
Simple format transformations often use an arrow, e.g.: cljs->clj
,
html->hiccup
, hex->bytes
.
Make side effects explicit
Clojure does a great job separating value, state, and
identity.
Clojure programmers should, too. If a function changes state or has
side effects, its name should reflect it. Functions that mutate
state like swap!
and reset!
end with a bang. Side effects hiding
elsewhere should also be explicit: if format-page
saves a file to
disk, it should be format-and-save-page
(or even better, two
separate functions).
UPDATE: See also the Clojure Style Guide, a concise, comprehensive community-driven document with many more guidelines on clean Clojure.