Lambda Island Clojure Style Guide
This is the style we use on all projects under the lambdaisland banner, as well as projects by Gaiwan, assuming there isn't a client-specific style that we should stick to.
In general we stick to conventions used in the community at large, as encoded in the Clojure Style Guide. This page documents particularities of our style that are either different from the style guide, not covered by the style guide, or generally worth pointing out. It's a continuous work in progress, but mostly because we add or clarify entries. We avoid changing these conventions unless there are extremely compelling arguments to do so.
We avoid bike-shedding over these rules, and don't discuss them unless there are strong arguments that go beyond style and taste. They may be different than what you are used to or like, but that's not a reason to ignore or try to change them.
:import on the same line as the first entry
Always use a vector for requires and a list for imports
Wrap namespaces in a vector even when only loading them for side-effects, use the list notation for import even if it's the only class you're importing from a package.
:refer, ideally using the last part of the ns name
So if a namespace is named
com.acmeinc.widget then use
:as widget. Sometimes the last part is too generic though, and you need to use a different name that makes more sense e.g.
[hiccup.core :as hiccup].
Sometimes the last part is not unique, or not saying much to a person reading the code. In that case you can use a combined alias with hyphens, e.g.
[lambdaisland.regal.generator :as regal-generator].
Avoid single letter aliases, except for a few exceptions
Some namespaces that are very core to an application and used all over the place are better off having a shorter alias used consistently.
[datomic.api :as d]
[clojure.string :as str]
:refer :all except in a few cases where we always use
Clojure.test should always be required as
[clojure.test :refer :all], we want that DSL to be available in every test namespace unqualified.
For cljs/cljc file you should still require
clojure.test (it will be converted to
cljs.test by the compiler under the hood), but you need to use explicit refers.
[clojure.test :refer [deftest testing is are use-fixtures]]
The other exception is a
core-ext namespace as used in Kaocha. This namespace contains functions that you could imagine going into
clojure.core, such a
exception?,etc. This one we always load as
[kaocha.core-ext :refer :all].
Some particulars about dealing with cross-platform CLJC files.
Avoid splicing into maps
When you want a key/value to present in a map only for .clj or only for .cljs, then it can be tempting to do this.
This works, but it trips up tools based on rewrite-clj, because rewrite-clj treats this as a map with an odd number of forms. Instead do this.
Have a separate reader conditional for each top level form
Note that this advice is for top-level forms only. In nested forms you should do whatever makes the code more clear. Generally it's good to minimize the amount of reader conditionals.
Use a platform namespace
This way you largely avoid using reader conditionals in your
.cljc files.The idea is also that if we use this pattern judiciously across projects, eventually we can combine those into a sort of cross-platform standard library.
Always have a single newline between top-level forms. If you feel the need to use multiple newlines to separate sections of a namespace, then put a comment there instead, or consider if really the namespace needs to be split.
Don't get too clever with destructuring. Avoid destructuring multiple levels at once, or mixing destructuring styles in the same form.
Avoid using keywords inside a :keys vector. It works, but it's not idiomatic.
You can still use namespaced symbols to destructure namespaced keywords, you can also namespace :keys, e.g.