The Clojure Style Guide is pretty good overall. It's very detailed and most of its advice is solid. There are handful of places it makes bad recommendations or is missing some advice.
Many of these criticisms apply to the output of linters like clj-kondo as well.
The style guide says to prefer threading macros in all cases to heavy
form nesting. This can lead to code that's not very readable. Most
cases where you need to get rid of heavy nesting would be more
readable as a let
. The advantage of the threading macros is that it
lets you avoid choosing names for the intermediate values. In some
cases this is helpful, when the names you would have written are
obvious or just repeating the same thing, go ahead and use ->
instead if you can. But there are cases where the names clarify
things, and using ->
there would only obscure the code.
When in doubt, write the let
version next to the ->
version and
compare them side by side. Do the additional names in the let
version clarify the code or do they get in the way? That should be
the deciding factor.
The style guide says never to use if
when you only have one branch
but to use when
instead; this is hogwash. The point of when
is the
implicit do
it contains; this means that when
can be used to imply
side-effects. There are plenty of times you're using a single-branched
if
where no side-effects are present; using when
there would be
misleading.
There is no rationale given in the style guide for this rule, because
it doesn't make any sense. Making it about side-effects means that the
choice between when
and if
can communicate something meaningful to
the reader. Making it about when there's only one branch turns it into
a meaningless distinction.
This is a lisp convention that is much older than Clojure. If you find that Elisp programmers are more concerned with separating side-effects from values than you are, you should probably reconsider your position.
The style guide says to avoid :refer :all
. This is good advice 99%
of the time. However, you should use :refer :all
when writing tests
and bringing in the clojure.test
namespace. That one case is the
only reason :refer :all
was even implemented.
I hate to resort to this, but I'm the one who implemented this functionality, so I think that means my opinion carries more weight than yours here.
You should focus on making code more readable, and empty?
is more
readable than seq
. If you can swap the branches of an if
to avoid
the double-negative of not
+empty?
that's great, but ignore the
advice to use seq
as a predicate. Using not-empty
as a predicate
works great too.
Look, if you want to sort your requires, that's fine. But that doesn't mean everyone should do it. The worst thing is when you encounter a mostly-sorted list of require clauses and you look for a specific ns and it doesn't appear to be there, but that's just because you thought it was sorted and that one ns was out of order.
Don't assume require clauses are sorted.
Clojure allows you to use square brackets when importing Java classes. This is a mistake because it results in bad indentation. For example:
;; good
(:import (java.io File
FileOutputStream
PrintWriter
InputStream))
;; bad
(:import [java.io File
FileOutputStream
PrintWriter
InputStream])
The rationale is that Clojure indentation treats lists differently from vectors; in a vector all the elements are assumed to be roughly "peers" to each other, and of equal importance, but in a list, the first element is interpreted as being special, and all the subsequent elements are lined up under the second element. This is desirable in imports because the first element is the package, and the remaining elements are classes, so you want the classes to be lined up together.