- Notifications
You must be signed in to change notification settings - Fork282
A community coding style guide for the Clojure programming language
bbatsov/clojure-style-guide
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Role models are important.
Tip | You can find a beautiful version of this guide with much improved navigation athttps://guide.clojure.style. |
This Clojure style guide recommends best practices so that real-world Clojureprogrammers can write code that can be maintained by other real-world Clojureprogrammers. A style guide that reflects real-world usage gets used, and astyle guide that holds to an ideal that has been rejected by the people it issupposed to help risks not getting used at all — no matter how good it is.
The guide is separated into several sections of related rules. we’vetried to add the rationale behind the rules (if it’s omitted, we’veassumed that it’s pretty obvious).
We didn’t come up with all the rules out of nowhere; they are mostlybased on the experience of the style guide’s editors,feedback and suggestions from numerous members of the Clojure community, andvarious highly regarded Clojure programming resources, such as"Clojure Programming"and"The Joy of Clojure".
Nothing written here is set in stone.This style guide evolves over time as additional conventions areidentified and past conventions are rendered obsolete by changes inClojure itself.
Note | Clojure’s developers also maintain a list ofcodingguidelines for libraries.[1]They were one of the sources of inspiration for the document, you’recurrently reading. |
You can generate a PDF copy of this guide usingAsciiDoctor PDF, and an HTML copywithAsciiDoctor using the following commands:
# Generates README.pdfasciidoctor-pdf -a allow-uri-read README.adoc# Generates README.htmlasciidoctor
Tip | Install the gem install rouge |
Programs must be written for people to read, and only incidentally for machines to execute.
Structure and Interpretation of Computer Programs
It’s common knowledge that code is read much more often than it is written.The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Clojure code.They are also meant to reflect real-world usage of Clojure instead of a random ideal. When we had to choose between a very established practiceand a subjectively better alternative we’ve opted to recommend the established practice.[2]
There are some areas in which there is no clear consensus in the Clojure community regarding a particular style (like semantic indentation vs fixed indentation, semantic comments vs uniform comments, etc).In such scenarios all popular styles are acknowledged and it’s up to you to pick one and apply it consistently.
Fortunately Clojure is a Lisp, and Lisps are fundamentallysimple. Even though this guide was created a few years after Clojure(the first version was published in early 2013), you could see that mostClojure code in the wild was fairly uniform. We attribute this toboth the simplicity we already mentioned and to the fact that since day1 Clojurists adopted many of the style conventions of otherestablished Lisp dialects (e.g. Common Lisp and Scheme). This madethe work on this guide fairly easy and straight-forward, especially compared tothe massive exercise in frustration that was theCommunity Ruby Style Guide.[3]
Clojure is famously optimized for simplicity and clarity. We’d like to believe that this guide is going to help you optimize for maximumsimplicity and clarity.
A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines.
A style guide is about consistency.[4]Consistency with this style guide is important.Consistency within a project is more important.Consistency within one class or method is the most important.
However, know when to be inconsistent — sometimes style guide recommendations just aren’t applicable. When in doubt, use your best judgment.Look at other examples and decide what looks best. And don’t hesitate to ask!
In particular: do not break backwards compatibility just to comply with this guide!
Some other good reasons to ignore a particular guideline:
When applying the guideline would make the code less readable, even for someone who is used to reading code that follows this style guide.
To be consistent with surrounding code that also breaks it (maybe for historic reasons) — although this is also an opportunity to clean up someone else’s mess (in true XP style).
Because the code in question predates the introduction of the guideline and there is no other reason to be modifying that code.
When the code needs to remain compatible with older versions of Clojure that don’t support the feature recommended by the style guide.
Translations of the guide are available in the following languages:
Note | These translations are not maintained by our editor team, so their qualityand level of completeness may vary. The translated versions of the guide oftenlag behind the upstream English version. |
Nearly everybody is convinced that every style but their own isugly and unreadable. Leave out the "but their own" and they’reprobably right…
Where feasible, avoid making lines longer than 80 characters.
A lot of people these days feel that a maximum line length of 80 characters isjust a remnant of the past and makes little sense today. After all - moderndisplays can easily fit 200+ characters on a single line. Still, there are someimportant benefits to be gained from sticking to shorter lines of code.
First, and foremost - numerous studies have shown that humans read much fastervertically and very long lines of text impede the reading process. As notedearlier, one of the guiding principles of this style guide is to optimize thecode we write for human consumption.
Additionally, limiting the required editor window width makes it possible tohave several files open side-by-side, and works well when using code reviewtools that present the two versions in adjacent columns.
The default wrapping in most tools disrupts the visual structure of the code,making it more difficult to understand. The limits are chosen to avoid wrappingin editors with the window width set to 80, even if the tool places a markerglyph in the final column when wrapping lines. Some web based tools may notoffer dynamic line wrapping at all.
Some teams strongly prefer a longer line length. For code maintained exclusivelyor primarily by a team that can reach agreement on this issue, it is okay toincrease the line length limit up to 100 characters, or all the way upto 120 characters. Please, restrain the urge to go beyond 120 characters.
Usespaces for indentation. No hard tabs.
Use 2 spaces to indent the bodies offorms that have body parameters. This covers alldef
forms, specialforms and macros that introduce local bindings (e.g.loop
,let
,when-let
) and many macros likewhen
,cond
,as->
,cond->
,case
,with-*
, etc.
;; good(when something (something-else))(with-out-str (println"Hello,") (println"world!"));; bad - four spaces(when something (something-else));; bad - one space(with-out-str (println"Hello,") (println"world!"))
Vertically align function (macro) arguments spanning multiple lines.
;; good(filter even? (range110));; bad - argument aligned with function name (one space indent)(filter even? (range110));; bad - two space indent(filter even? (range110))
The reasoning behind this guideline is pretty simple - the arguments areeasier to process by the human brain if they stand out and stick together.
Note | Generally, you should stick to the formatting outlined in the previousguideline, unless you’re limited by the available horizontal space. |
Use a single space indentation for function (macro) argumentswhen there are no arguments on the same line as the function name.
;; good(filter even? (range110))(or ala bala portokala);; bad - two-space indent(filter even? (range110))(or ala bala portokala)
This may appear like some weird special rule to people without Lisp background, but thereasoning behind it is quite simple. Function calls arenothing but regular list literals and normally those are aligned in the same way asother collection type literals when spanning multiple lines:
;; list literal(123);; vector literal[123];; set literal#{123}
Admittedly, list literals are not very common in Clojure, that’s why it’s understandablethat for many people lists are nothing but an invocation syntax.
As a side benefit, function arguments are still aligned in this scenario as well. Theyjust happen to accidentally be aligned with the function name as well.
The guidelines to indent differently macros with body forms fromall other macro and function calls are collectively known as"semantic indentation". Simply put, this means that the codeis indented differently, so that the indentation would give thereader of the code some hints about its meaning.
The downside of this approach is that requires Clojure code formatters to besmarter. They either need to processmacro
arglists and rely on the factthat people named their parameters consistently, or process some additionalindentation metadata.
Some people in the Clojure community have argued that’s not worth it andthat everything should simply be indented in the same fashion. Here area few examples:
;;; Fixed Indentation;;;; macros(when something (something-else))(with-out-str (println"Hello,") (println"world!"));; function call spanning two lines(filter even? (range110));; function call spanning three lines(filter even? (range110))
This suggestion has certainly gotten some ground in the community, but it alsogoes against much of the Lisp tradition and one of the primary goals of this style guide -namely to optimize code for human consumption.
There’s one exception to the fixed indentation rule - data lists (those that are not a function invocation):
;;; Fixed Indentation;;;; list literals;; we still do(123456);; and(123456);; instead of(123456);; or(123456)
This makes sure that lists are consistent with how other collection types are normally indented.
Vertically alignlet
(andlet
-like) bindings.
;; good(let [thing1"some stuff" thing2"other stuff"] (foo thing1 thing2));; bad(let [thing1"some stuff" thing2"other stuff"] (foo thing1 thing2))
Align vertically map keys.
;; good{:thing1 thing1:thing2 thing2};; bad{:thing1 thing1:thing2 thing2};; bad{:thing1 thing1:thing2 thing2}
Use Unix-style line endings.[5]
Tip | If you’re using Git you might want to add the followingconfiguration setting to protect your project from Windows lineendings creeping in: $ git config --global core.autocrlftrue |
End each file with a newline.
Tip | This should be done by through editor configuration, not manually. |
If any text precedes an opening bracket((
,{
and[
) or follows a closing bracket()
,}
and]
), separate thattext from that bracket with a space. Conversely, leave no space afteran opening bracket and before following text, or after preceding textand before a closing bracket.
;; good(foo (bar baz) quux);; bad(foo(bar baz)quux)(foo ( bar baz ) quux)
Syntactic sugar causes semicolon cancer.
Don’t use commas between the elements of sequential collection literals.
;; good[123](123);; bad[1,2,3](1,2,3)
Consider enhancing the readability of map literals via judicious useof commas and line breaks.
;; good{:name"Bruce Wayne":alter-ego"Batman"};; good and arguably a bit more readable{:name"Bruce Wayne":alter-ego"Batman"};; good and arguably more compact{:name"Bruce Wayne",:alter-ego"Batman"}
Place trailing parentheses on a single line instead of distinct lines.
;; good; single line(when something (something-else));; bad; distinct lines(when something (something-else))
An exception to the rule is a rich comment for REPL-oriented programming,lest gathering the comment’s closing parenthesis make the last form in the comment harder to use.A rich comment’s closing parenthesis may be on the next line.
;; good; rich comment ends on the next line(comment (range5) (interpose '* (range5)) (->> (range5) (interpose '*)) (= *1 *2) )
Use a single empty line between top-level forms.
;; good(defx...)(defnfoo...);; bad(defx...)(defnfoo...);; bad(defx...)(defnfoo...)
An exception to the rule is the grouping of relateddef
s together.
;; good(defmin-rows10)(defmax-rows20)(defmin-cols15)(defmax-cols30)
Do not place blank lines in the middle of a function ormacro definition. An exception can be made to indicate grouping ofpairwise constructs as found in e.g.let
andcond
, in case those don’tfit on the same line.
;; good(defnfibo-iter ([n] (fibo-iter01 n)) ([curr nxt n] (cond (zero? n) curr:else (recur nxt (+' curr nxt) (dec n)))));; okay - the line break delimits a cond pair(defnfibo-iter ([n] (fibo-iter01 n)) ([curr nxt n] (cond (zero? n) curr:else (recur nxt (+' curr nxt) (dec n)))));; bad(defnfibo-iter ([n] (fibo-iter01 n)) ([curr nxt n] (cond (zero? n) curr:else (recur nxt (+' curr nxt) (dec n)))))
Occasionally, it might seem like a good idea to add a blank linehere and there in a longer function definition, but if you get to thispoint you should also consider whether this long function isn’t doingtoo much and could potentially be broken down.
Avoid trailing whitespace.
Use one file per namespace and one namespace per file.
;; good(nsfoo.bar);; bad(nsfoo.bar)(nsbaz.qux);; bad(in-ns quux.quuz)(in-ns quuz.corge);; bad(nsfoo.bar) or (in-ns foo.bar) in multiple files
Avoid single-segment namespaces.
;; good(nsexample.ns);; bad(nsexample)
Namespaces exist to disambiguate names. Using a single segmentnamespace puts you in direct conflict with everyone else using singlesegment namespaces, thus making it more likely you will conflict withanother code base.
In practice this means that libraries should never use single-segmentnamespace to avoid namespace conflicts with other libraries.Within your own private app of course, you can do whatever you like.
Tip | It’s common practice to use the conventiondomain.library-name orlibrary-name.core for libraries with a single namespace in them.Read on for more coverage of the namespace naming topic. |
There areother reasonswhy might want to avoid single-segment namespaces, so you shouldthink long and hard before making any use of them.
Avoid the use of overly long namespaces (i.e., more than 5 segments).
Start every namespace with a comprehensivens
form, comprised ofrefer
s,require
s, andimport
s, conventionally in that order.
(nsexamples.ns (:refer-clojure:exclude [next replace remove]) (:require [clojure.string:as s:refer [blank?]]) (:import java.util.Date))
When there are multiple dependencies, you may want give each one its own line.This facilitates sorting, readability, and cleaner diffs for dependency changes.
;; better(nsexamples.ns (:require [clojure.string:as s:refer [blank?]] [clojure.set:as set] [clojure.java.shell:as sh]) (:import java.util.Date java.text.SimpleDateFormat [java.util.concurrent Executors LinkedBlockingQueue]));; good(nsexamples.ns (:require [clojure.string:as s:refer [blank?]] [clojure.set:as set] [clojure.java.shell:as sh]) (:import java.util.Date java.text.SimpleDateFormat [java.util.concurrent Executors LinkedBlockingQueue]));; bad(nsexamples.ns (:require [clojure.string:as s:refer [blank?]] [clojure.set:as set] [clojure.java.shell:as sh]) (:import java.util.Date java.text.SimpleDateFormat [java.util.concurrent Executors LinkedBlockingQueue]))
In thens
form prefer:require :as
over:require :refer
over:require:refer :all
. Prefer:require
over:use
; the latter form should beconsidered deprecated for new code.
;; good(nsexamples.ns (:require [clojure.zip:as zip]));; good(nsexamples.ns (:require [clojure.zip:refer [lefts rights]]));; acceptable as warranted(nsexamples.ns (:require [clojure.zip:refer:all]));; bad(nsexamples.ns (:use clojure.zip))
In thens
form, sort your requirements and imports. This facilitates readability and avoids duplication, especially when the list of required / imported namespaces is very long.
;; good(nsexamples.ns (:require [baz.core:as baz] [clojure.java.shell:as sh] [clojure.set:as set] [clojure.string:as s:refer [blank?]] [foo.bar:as foo]));; bad(nsexamples.ns (:require [clojure.string:as s:refer [blank?]] [clojure.set:as set] [baz.core:as baz] [foo.bar:as foo] [clojure.java.shell:as sh]))
Many core Clojure namespaces have idiomatic aliases that you’reencouraged to use within your projects - e.g. the most common way torequireclojure.string
is:[clojure.string :as str]
.
Note | This may appear to maskclojure.core.str , but it doesn’t. It’s expected thatclojure.core/str andclojure.string/* to be used in a namespace asstr andstr/whatever without conflict. |
;; good(ns... (:require [clojure.string:as str] ...)(str/join ...);; not as good - just be idiomatic and use as `str/`(ns... (:require [clojure.string:as string] ...)(string/join ...)
As noted in the next section, it’s generally considered idiomatic to usean alias that is the last segment of the namespace, if that makes it unique,or else the two segments, typically dropping redundant parts likeclj
orcore
.
Amongst Clojure’s core and Contrib namespaces, the following namespaces haveidiomatic aliases following that pattern:
Namespace | Idiomatic Alias |
clojure.datafy | datafy |
clojure.edn | edn |
clojure.java.io | io |
clojure.math | math |
clojure.set | set |
clojure.walk | walk |
clojure.zip | zip |
clojure.core.async | async |
clojure.data.csv | csv |
clojure.data.xml | xml |
clojure.tools.cli | cli |
Then there are some core and Contrib namespaces that have shorter idiomatic aliases:
Namespace | Idiomatic Alias |
clojure.java.shell | sh |
clojure.pprint | pp |
clojure.spec.alpha | s |
clojure.string | str |
clojure.core.matrix | mat |
clojure.tools.logging | log |
clojure.core.protocols | p |
clojure.core.reducers | r |
And amongst commonly used community libraries, there are also many that havewidely-used, idiomatic aliases for several namespaces:
Namespace | Idiomatic Alias |
cheshire.core | json |
clj-yaml.core | yaml |
clj-http.client | http |
hugsql.core | sql |
java-time | time |
next.jdbc | jdbc |
Above we covered a handful of popular namespaces and their idiomatic aliases.You might have noticed that those are a bit inconsistent:
clojure.string
becomesstr
clojure.pprint
becomespp
clojure.walk
becomeswalk
clojure.spec.alpha
becomess
It’s clear that the one thing they have in common is that they aim to be concise, but still carry some meaning (aliasingclojure.walk
tow
wouldbe concise, but won’t carry much meaning).
But what to do about all the other namespaces out there that don’t have idiomatic aliases? Well, you better be consistent in your approach to deriving aliases for them,otherwise the people working on a shared Clojure codebase are going to experience a great deal of confusion. Here are a few rules that you should follow.[6]
Make the alias the same as the namespace name with the leading parts removed.
(nscom.example.application (:require [clojure.java.io:as io] [clojure.reflect:as reflect]))
Keep enough trailing parts to make each alias unique.
[clojure.data.xml:as data.xml][clojure.xml:as xml]
TipYes, namespace aliases can have dots in them. Make good use of them. Eliminate redundant words such as "core" and "clj" in aliases.
[clj-time.core:as time][clj-time.format:as time.format]
Across a project, it’s good to be consistent with namespace aliases; e.g., don’t requireclojure.string
asstr
in one namespace butstring
in another.If you follow the previous two guidelines you’re basically covered, but if you opt for custom namespace aliasing scheme it’s still important to apply itconsistently within your projects.
The only real difficulties in programming are cache invalidation andnaming things.
When naming namespaces favor the following two schemas:
project.module
organization.project.module
When you’re following theproject.module
naming scheme and your projecthas a single (implementation) namespace it’s common to name itproject.core
.Avoid theproject.core
name in all other cases, as more informative namesare always a better idea.
Uselisp-case
in composite namespace segments (e.g.bruce.project-euler
).
Note | Many non-Lisp programming communities refer tolisp-case askebab-case , but we all know that Lisp existed way before kebabwas invented. |
Uselisp-case
for function and variable names.
;; good(defsome-var...)(defnsome-fun...);; bad(defsomeVar...)(defnsomefun...)(defsome_fun...)
UseCapitalCase
for protocols, records, structs, and types. (Keepacronyms like HTTP, RFC, XML uppercase.)
Note | CapitalCase is also known asUpperCamelCase, `CapitalWords andPascalCase . |
The names of predicate methods (methods that return a boolean value)should end in a question mark(e.g.,even?
).
;; good(defnpalindrome?...);; bad(defnpalindrome-p...); Common Lisp style(defnis-palindrome...); Java style
The names of functions/macros that are not safe in STM transactionsshould end with an exclamation mark (e.g.reset!
).
Use->
instead ofto
in the names of conversion functions.
;; good(defnf->c...);; not so good(defnf-to-c...)
Useearmuffs
for things intended for rebinding (ie. are dynamic).
;; good(def ^:dynamic *a*10);; bad(def ^:dynamica10)
Don’t use a special notation for constants; everything is assumed a constantunless specified otherwise.
;; good(defmax-size10);; bad(defMAX-SIZE10); Java style(def+max-size+10); Common Lisp style, global constant(def *max-size*10); Common Lisp style, global variable
Note | Famously*clojure-version* defies this convention, but you shouldtreat this naming choice as a historical oddity and not as an example tofollow. |
Use_
for destructuring targets and formal argument names whosevalue will be ignored by the code at hand.
;; good(let [[a b _ c] [1234]] (println a b c))(dotimes [_3] (println"Hello!"));; bad(let [[a b c d] [1234]] (println a b d))(dotimes [i3] (println"Hello!"))
However, when it can help the understanding of your code, it can be useful to explicitly name unused arguments or maps you’re destructuring from. In this case, prepend the name with an underscore to explicitly signal that the variable is supposed to be unused.
;; good(defnmyfun1 [context _] (assoc context:foo"bar"))(defnmyfun2 [context {:keys [id]}] (assoc context:user-id id));; better(defnmyfun1 [context _user] (assoc context:foo"bar"))(defnmyfun2 [context {:keys [id]:as _user}] (assoc context:user-id id))
Followclojure.core
's example for idiomatic names likepred
andcoll
.
in functions:
f
,g
,h
- function inputn
- integer input usually a sizeindex
,i
- integer indexx
,y
- numbersxs
- sequencem
- mapk
,ks
- key, keysv
,vs
- value, values (as in a key/value pair)s
- string inputre
- regular expressionsym
- symbolcoll
- a collectionpred
- a predicate closure& more
- variadic inputxf
- xform, a transducerns
- namespace[7]
in macros:
expr
- an expressionbody
- a macro bodybinding
- a macro binding vector
in methods (when specified in
defprotocol
,deftype
,defrecord
,reify
, etc):this
- for the first argument, indicating a reference to the object - or alternatively, a consistent name which describes the object
Optionally omit the new line between the function name and argumentvector fordefn
when there is no docstring.
;; good(defnfoo [x] (bar x));; good(defnfoo [x] (bar x));; bad(defnfoo [x] (bar x))
Place thedispatch-val
of a multimethod on the same line as thefunction name.
;; good(defmethodfoo:bar [x] (baz x))(defmethodfoo:bar [x] (baz x));; bad(defmethodfoo:bar [x] (baz x))(defmethodfoo:bar [x] (baz x))
Optionally omit the new line between the argument vector and a shortfunction body.
;; good(defnfoo [x] (bar x));; good for a small function body(defnfoo [x] (bar x));; good for multi-arity functions(defnfoo ([x] (bar x)) ([x y] (if (predicate? x) (bar x) (baz x))));; bad(defnfoo [x] (if (predicate? x) (bar x) (baz x)))
Indent each arity form of a function definition vertically aligned with itsparameters.
;; good(defnfoo"I have two arities." ([x] (foo x1)) ([x y] (+ x y)));; bad - extra indentation(defnfoo"I have two arities." ([x] (foo x1)) ([x y] (+ x y)))
Sort the arities of a functionfrom fewest to most arguments. The common case of multi-arityfunctions is that some K arguments fully specifies the function’sbehavior, and that arities N < K partially apply the K arity, andarities N > K provide a fold of the K arity over varargs.
;; good - it's easy to scan for the nth arity(defnfoo"I have two arities." ([x] (foo x1)) ([x y] (+ x y)));; okay - the other arities are applications of the two-arity(defnfoo"I have two arities." ([x y] (+ x y)) ([x] (foo x1)) ([x y z & more] (reduce foo (foo x (foo y z)) more)));; bad - unordered for no apparent reason(defnfoo ([x]1) ([x y z] (foo x (foo y z))) ([x y] (+ x y)) ([w x y z & more] (reduce foo (foo w (foo x (foo y z))) more)))
Avoid functions longer than 10 LOC (lines of code). Ideally, mostfunctions will be shorter than 5 LOC.
Avoid parameter lists with more than three or four positional parameters.
Prefer function pre and post conditions to checks inside a function’s body.
;; good(defnfoo [x] {:pre [(pos? x)]} (bar x));; bad(defnfoo [x] (if (pos? x) (bar x) (throw (IllegalArgumentException."x must be a positive number!")))
Avoid the use of namespace-manipulating functions likerequire
andrefer
. They are entirely unnecessary outside of a REPLenvironment.
Avoid forward references. They are occasionally necessary, but such occasionsare rare in practice.
Usedeclare
to enable forward references when forward references arenecessary.
Prefer higher-order functions likemap
toloop/recur
.
Don’t shadowclojure.core
names with local bindings.
;; bad - clojure.core/map must be fully qualified inside the function(defnfoo [map]...)
Usealter-var-root
instead ofdef
to change the value of a var.
;; good(defthing1); value of thing is now 1; do some stuff with thing(alter-var-root #'thing (constantlynil)); value of thing is now nil;; bad(defthing1); do some stuff with thing(defthingnil); value of thing is now nil
Useseq
as a terminating condition to test whether a sequence isempty (this technique is sometimes callednil punning).
;; good(defnprint-seq [s] (when (seq s) (prn (first s)) (recur (rest s))));; bad(defnprint-seq [s] (when-not (empty? s) (prn (first s)) (recur (rest s))))
Prefervec
overinto
when you need to convert a sequence into a vector.
;; good(vec some-seq);; bad(into [] some-seq)
Use theboolean
function if you need to convert something to an actual boolean value (true
orfalse
).
;; good(boolean (foo bar));; bad(if (foo bar)truefalse)
Note | Don’t forget that the only values in Clojure that are "falsey" arefalse andnil . Everything elsewill evaluate totrue when passed to theboolean function. |
You’ll rarely need an actual boolean value in Clojure, but it’s useful to know how to obtain one when you do.
Usewhen
instead ofif
with just the truthy branch, as in(if condition (something…))
or(if … (do …))
.
;; good(when pred (foo) (bar));; bad(if pred (do (foo) (bar)))
Useif-let
instead oflet
+if
.
;; good(if-let [result (foo x)] (something-with result) (something-else));; bad(let [result (foo x)] (if result (something-with result) (something-else)))
Usewhen-let
instead oflet
+when
.
;; good(when-let [result (foo x)] (do-something-with result) (do-something-more-with result));; bad(let [result (foo x)] (when result (do-something-with result) (do-something-more-with result)))
Usewhen-not
instead of(when (not …) …)
.
;; good(when-not pred (foo) (bar));; bad(when (not pred) (foo) (bar))
Usewhen-not
instead of(if-not … (do …))
.
;; good(when-not pred (foo) (bar));; bad(if-not pred (do (foo) (bar)))
Preferprintf
over(print (format …))
.
;; good(printf"Hello, %s!\n" name);; ok(println (format"Hello, %s!" name))
When doing comparisons, leverage the fact that Clojure’s functions<
,>
, etc. accept a variable number of arguments.
;; good(<5 x10);; bad(and (> x5) (< x10))
Prefer%
over%1
in function literals with only one parameter.
;; good#(Math/round %);; bad#(Math/round %1)
Prefer%1
over%
in function literals with more than one parameter.
;; good#(Math/pow %1 %2);; bad#(Math/pow % %2)
Don’t wrap functions in anonymous functions when you don’t need to.
;; good(filter even? (range110));; bad(filter #(even? %) (range110))
Don’t use function literals if the function body will consist ofmore than one form.
;; good(fn [x] (println x) (* x2));; bad (you need an explicit do form)#(do (println %) (* %2))
Prefer anonymous functions overcomplement
,comp
andpartial
, as this resultsin simpler code most of the time.[8]
;; good(filter #(not (some-pred? %)) coll);; okish(filter (complement some-pred?) coll)
;; Assuming `(:require [clojure.string :as str])`...;; good(map #(str/capitalize (str/trim %)) ["top"" test"]);; okish(map (comp str/capitalize str/trim) ["top"" test"])
comp
is quite useful when composing transducer chains, though.
;; good(defxf (comp (filter odd?) (map inc) (take5)))
;; good(map #(+5 %) (range110));; okish(map (partial +5) (range110))
Prefer the use of the threading macros->
(thread-first) and->>
(thread-last) to heavy form nesting.
;; good(-> [123] reverse (conj4) prn);; not as good(prn (conj (reverse [123])4));; good(->> (range110) (filter even?) (map (partial *2)));; not as good(map (partial *2) (filter even? (range110)))
Parentheses are not required when using the threading macros for functions having no argument specified, so use them only when necessary.
;; good(-> x fizz:foo first frob);; bad; parens add clutter and are not needed(-> x (fizz) (:foo) (first) (frob));; good, parens are necessary with an arg(-> x (fizz a b):foo first (frob x y))
The arguments to the threading macros->
(thread-first) and->>
(thread-last) should line up.
;; good(->> (range) (filter even?) (take5));; bad(->> (range) (filter even?) (take5))
Use:else
as the catch-all test expression incond
.
;; good(cond (neg? n)"negative" (pos? n)"positive":else"zero");; bad(cond (neg? n)"negative" (pos? n)"positive"true"zero")
Prefercondp
instead ofcond
when the predicate & expression don’tchange.
;; good(cond (= x10):ten (= x20):twenty (= x30):thirty:else:dunno);; much better(condp = x10:ten20:twenty30:thirty:dunno)
Prefercase
instead ofcond
orcondp
when test expressions arecompile-time constants.
;; good(cond (= x10):ten (= x20):twenty (= x30):forty:else:dunno);; better(condp = x10:ten20:twenty30:forty:dunno);; best(case x10:ten20:twenty30:forty:dunno)
Use short forms incond
and related. If not possible give visualhints for the pairwise grouping with comments or empty lines.
;; good(cond (test1) (action1) (test2) (action2):else (default-action));; ok-ish(cond;; test case 1 (test1) (long-function-name-which-requires-a-new-line (complicated-sub-form (-> 'which-spans multiple-lines)));; test case 2 (test2) (another-very-long-function-name (yet-another-sub-form (-> 'which-spans multiple-lines))):else (the-fall-through-default-case (which-also-spans 'multiple 'lines)))
Use aset
as a predicate when appropriate.
;; good(remove #{1} [012345]);; bad(remove #(= %1) [012345]);; good(count (filter #{\a \e \i \o \u}"mary had a little lamb"));; bad(count (filter #(or (= % \a) (= % \e) (= % \i) (= % \o) (= % \u))"mary had a little lamb"))
Use(inc x)
&(dec x)
instead of(+ x 1)
and(- x 1)
.
Use(pos? x)
,(neg? x)
&(zero? x)
instead of(> x 0)
,(< x 0)
&(= x 0)
.
Uselist*
instead of a series of nestedcons
invocations.
;; good(list*123 [45]);; bad(cons1 (cons2 (cons3 [45])))
Use the sugared Java interop forms.
;;; object creation;; good(java.util.ArrayList.100);; bad(new java.util.ArrayList100);;; static method invocation;; good(Math/pow210);; bad(. Math pow210);;; instance method invocation;; good(.substring"hello"13);; bad(."hello" substring13);;; static field access;; goodInteger/MAX_VALUE;; bad(. Integer MAX_VALUE);;; instance field access;; good(.someField some-object);; bad(. some-object someField)
Use the compact metadata notation for metadata that contains onlyslots whose keys are keywords and whose value is booleantrue
.
;; good(def ^:privatea5);; bad(def ^{:privatetrue}a5)
Denote private parts of your code.
;; good(defn-private-fun []...)(def ^:privateprivate-var...);; bad(defnprivate-fun []...); not private at all(defn ^:privateprivate-fun []...); overly verbose(defprivate-var...); not private at all
To access a private var (e.g. for testing), use the@#'some.ns/var
form.
Be careful regarding what exactly you attach metadata to.
;; we attach the metadata to the var referenced by `a`(def ^:privatea {})(meta a);=> nil(meta #'a);=> {:private true};; we attach the metadata to the empty hash-map value(defa ^:private {})(meta a);=> {:private true}(meta #'a);=> nil
It is better to have 100 functions operate on one data structurethan to have 10 functions operate on 10 data structures.
Avoid the use of lists for generic data storage (unless a list isexactly what you need).
Prefer the use of keywords for hash keys.
;; good{:name"Bruce":age30};; bad{"name""Bruce""age"30}
Prefer the use of the literal collection syntax whereapplicable. However, when defining sets, only use literal syntaxwhen the values are compile-time constants.
;; good[123]#{123}(hash-set (func1) (func2)); values determined at runtime;; bad(vector123)(hash-set123)#{(func1) (func2)}; will throw runtime exception if (func1) = (func2)
Avoid accessing collection members by index whenever possible.
Prefer the use of keywords as functions for retrieving values frommaps, where applicable.
(defm {:name"Bruce":age30});; good(:name m);; more verbose than necessary(get m:name);; bad - susceptible to NullPointerException(m:name)
Leverage the fact that most collections are functions of their elements.
;; good(filter #{\a \e \o \i \u}"this is a test");; bad - too ugly to share
Leverage the fact that keywords can be used as functions of a collection.
((juxt:a:b) {:a"ala":b"bala"})
Avoid the use of transient collections, except forperformance-critical portions of the code.
Avoid the use of Java collections.
Avoid the use of Java arrays, except for interop scenarios andperformance-critical code dealing heavily with primitive types.
Don’t use the interop syntax toconstruct type and record instances.deftype
anddefrecord
automatically create constructor functions. Use those instead ofthe interop syntax, as they make it clear that you’re dealing with adeftype
or adefrecord
. Seethisarticlefor more details.
(defrecordFoo [a b])(deftypeBar [a b]);; good(->Foo12)(map->Foo {:b4:a3})(->Bar12);; bad(Foo.12)(Bar.12)
Note thatdeftype
doesn’t define themap->Type
constructor. It’s available only for records.
Add custom type/record constructors when needed (e.g. to validateproperties on record creation). Seethisarticlefor more details.
(defrecordCustomer [id name phone email])(defnmake-customer"Creates a new customer record." [{:keys [name phone email]}] {:pre [(string? name) (valid-phone? phone) (valid-email? email)]} (->Customer (next-id) name phone email))
Feel free to adopt whatever naming convention or structure you’d like for such custom constructors.
Don’t override the auto-generated type/record constructor functions.People expect them to have a certain behaviour and changing this behaviourviolates the principle of least surprise. Seethisarticlefor more details.
(defrecordFoo [num]);; good(defnmake-foo [num] {:pre [(pos? num)]} (->Foo num));; bad(defn->Foo [num] {:pre [(pos? num)]} (Foo. num))
Consider wrapping all I/O calls with theio!
macro to avoid nastysurprises if you accidentally end up calling such code in atransaction.
Avoid the use ofref-set
whenever possible.
(defr (ref0));; good(dosync (alter r +5));; bad(dosync (ref-set r5))
Try to keep the size of transactions (the amount of work encapsulated in them)as small as possible.
Avoid having both short- and long-running transactions interactingwith the same Ref.
Usesend
only for actions that are CPU bound and don’t block on I/Oor other threads.
Usesend-off
for actions that might block, sleep, or otherwise tieup the thread.
Avoid atom updates inside STM transactions.
Try to useswap!
rather thanreset!
, where possible.
(defa (atom0));; good(swap! a +5);; not as good(reset! a5)
Prefer math functions fromclojure.math
over (Java) interop or rolling your own.
;; good(clojure.math/pow25);; okish(Math/pow25)
The JDK packagejava.lang.Math
provides access to many useful mathfunctions. Prior to version 1.11, Clojure relied on using these via interop, but this hadissues with discoverability, primitive performance, higher order application,and portability. The newclojure.math
namespace provides wrapper functions forthe methods available injava.lang.Math
forlong
anddouble
overloads with fastprimitive invocation.
Prefer string manipulation functions fromclojure.string
over Java interop or rolling your own.
;; good(clojure.string/upper-case"bruce");; bad(.toUpperCase"bruce")
Note | Several new functions were added toclojure.string in Clojure1.8 (index-of ,last-index-of ,starts-with? ,ends-with? andincludes? ). You should avoid using those if you need to supportolder Clojure releases. |
Reuse existing exception types. Idiomatic Clojure code — when it doesthrow an exception — throws an exception of a standard type(e.g.java.lang.IllegalArgumentException
,java.lang.UnsupportedOperationException
,java.lang.IllegalStateException
,java.io.IOException
).
Favorwith-open
overfinally
.
Don’t write a macro if a function will do.
Create an example of a macro usage first and the macro afterwards.
Break complicated macros into smaller functions whenever possible.
A macro should usually just provide syntactic sugar and the core ofthe macro should be a plain function. Doing so will improvecomposability.
Prefer syntax-quoted forms over building lists manually.
In this section we’ll go over some common metadata for namespaces andvars that Clojure development tools can leverage.
The most common way to document when a public API was added to alibrary is via the:added
metadata.
(def ^{:added"0.5"}foo42)(nsfoo.bar"A very useful ns." {:added"0.8"})(defn ^{:added"0.5"}foo (bar))
Tip | If you’re into SemVer, it’s a good idea to omit the patch version.This means you should use0.5 instead of0.5.0 . This appliesfor all metadata data that’s version related. |
The most common way to document when a public API was changed in alibrary is via the:changed
metadata. This metadata makes sense only forvars and you should be using it sparingly, as changing the behavior ofa public API is generally a bad idea.
Still, if you decide to do it, it’s best to make that clear to the APIusers.
(def ^{:added"0.5":changed"0.6"}foo43)
The most common way to mark deprecated public APIs is via the:deprecated
metadata. Normally you’d use as the value the version in which somethingwas deprecated in case of versioned software (e.g. a library) or simplytrue
in the case of unversioned software (e.g. some web application).
;;; good;;;; in case we have a version(def ^{:deprecated"0.5"}foo"Use `bar` instead."42)(nsfoo.bar"A deprecated ns." {:deprecated"0.8"})(defn ^{:deprecated"0.5"}foo (bar));; otherwise(defn ^:deprecatedfoo (bar));;; bad;;;; using the docstring to signal deprecation(deffoo"DEPRECATED: Use `bar` instead."42)(nsfoo.bar"DEPRECATED: A deprecated ns.")
Often you’d combine:deprecated
with:superseded-by
, as there would besome newer API that supersedes whatever got deprecated.
Typically for vars you’ll use a non-qualified name if the replacement livesin the same namespace, and a fully-qualified name otherwise.
;; in case we have a version(def ^{:deprecated"0.5":superseded-by"bar"}foo"Use `bar` instead."42)(nsfoo.bar"A deprecated ns." {:deprecated"0.8":superseded-by"foo.baz"})(defn ^{:deprecated"0.5":superseded-by"bar"}foo (bar));; otherwise(defn ^{:deprecatedtrue:superseded-by"bar"}foo (bar))
Tip | You can also consider adding:supersedes metadata to the newer APIs, basically the inverse of:superseded-by . |
From time to time you might want to point out some related vars/namespaces that the users of your library might be interested in.The most common way to do so would be via the:see-also
metadata, which takes a vector of related items.When talking about vars - items in the same namespace don’t need to fully qualified.
;; refers to vars in the same ns(def ^{:see-also ["bar""baz"]}foo"A very useful var."42);; refers to vars in some other ns(defn ^{:see-also ["top.bar""top.baz"]}foo (bar))
Note | Many Clojure programming tools will also try to extract references to other vars from the docstring, but it’s bothsimpler and more explicit to use the:see-also metadata instead. |
Documentation tools likeCodox likecljdoc recognize:no-doc
metadata.When a var or a namespace has:no-doc
metadata, it indicates to these tools that it should be excluded from generated API docs.
To exclude an entire namespace from API docs:
(ns ^:no-docmy-library.impl"Internal implementation details")...
To exclude vars within a documented namespace:
(nsmy-library.api);; private functions do not get documented(defn-clearly-private []...);; nor do public functions with :no-doc metadata(defn ^:no-docshared-helper []...);; this function will be documented(defnapi-fn1"I am useful to the public" []...)
Unlike other Lisp dialects, Clojure doesn’t have a standard metadata format to specify the indentation of macros.CIDER proposed a tool-agnosticindentation specification based on metadata in 2015.[9] Here’s a simple example:
;; refers to vars in the same ns(defmacrowith-in-str"[DOCSTRING]" {:style/indent1} [s & body]...cutforbrevity...)
This instructs the indentation engine that this is a macro with one ordinary parameter and a body after it.
;; without metadata (indented as a regular function)(dop-iin-str some-string foo bar baz);; with metadata (indented as macro with one special param and a body)(with-in-str some-string foo bar baz)
Unfortunately, as of 2020 there’s still no widespread adoption of:style/indent
and many editors and IDEs would justhardcode the indentation rules for common macros.
Note | This approach to indentation ("semantic indentation") is a contested topic in the Clojure community, due to theneed for the additional metadata and tooling support. Despite the long tradition of that approach in the Lisp communityin general, some people argue to just stop treating functions and macros differently and simply indent everything with a fixedindentation.This article is one popular presentation of that alternative approach. |
Good code is its own best documentation. As you’re about to add acomment, ask yourself, "How can I improve the code so that thiscomment isn’t needed?" Improve the code and then document it to makeit even clearer.
Endeavor to make your code as self-explanatory as possible.If you fail to achieve this follow the rest of the guidelines in this section.
Write heading comments with at least four semicolons. Those typically serve to outline/separatemajor section of code, or to describe important ideas. Often you’d have a section comment followedby a bunch of top-level comments.
;;;; Section Comment/Heading;;; Foo...;;; Bar...;;; Baz...
Write top-level comments with three semicolons.
;;; I'm a top-level comment.;;; I live outside any definition.(defnfoo [])
Note | While the classic Lisp tradition dictates the use of;;; fortop-level comments, you’ll find plenty of Clojure code in the wildthat’s using;; or even; . |
Write comments on a particular fragment of code before that fragmentand aligned with it, using two semicolons.
(defnfoo [x];; I'm a line/code fragment comment.x)
Note | While the classic Lisp tradition dictates the use of;; forline comments, you’ll find plenty of Clojure code in the wildthat’s using only; . |
Write margin comments with one semicolon.
(defnfoo [x]x; I'm a line/code fragment comment. )
Avoid using those in situations that would result in hanging closing parentheses.
Always have at least one space between the semicolonand the text that follows it.
;;;; Frob Grovel;;; This section of code has some important implications:;;; 1. Foo.;;; 2. Bar.;;; 3. Baz.(defnfnord [zarquon];; If zob, then veeblefitz. (quux zot mumble; Zibblefrotz. frotz))
Comments longer than a word begin with a capital letter and usepunctuation. Separate sentences withone space.
;; This is a good comment.;; this is a bad comment
Obviously punctuation is not the most important thing abouta comment, but a bit of extra effort results in better experiencefor the readers of our comments.
Keep existing comments up-to-date. An outdated comment is worse than no commentat all.
Prefer the use of the#_
reader macro over a regular comment whenyou need to comment out a particular form.
;; good(+ foo #_(bar x) delta);; bad(+ foo;; (bar x) delta)
Good code is like a good joke - it needs no explanation.
Avoid writing comments to explain bad code. Refactor the code tomake it self-explanatory. ("Do, or do not. There is no try." --Yoda)
Annotations should usually be written on the line immediately abovethe relevant code.
;; good(defnsome-fun [];; FIXME: Replace baz with the newer bar. (baz));; bad;; FIXME: Replace baz with the newer bar.(defnsome-fun [] (baz))
The annotation keyword is followed by a colon and a space, then a notedescribing the problem.
;; good(defnsome-fun [];; FIXME: Replace baz with the newer bar. (baz));; bad - no colon after annotation(defnsome-fun [];; FIXME Replace baz with the newer bar. (baz));; bad - no space after colon(defnsome-fun [];; FIXME:Replace baz with the newer bar. (baz))
If multiple lines are required to describe the problem, subsequentlines should be indented as much as the first one.
;; good(defnsome-fun [];; FIXME: This has crashed occasionally since v1.2.3. It may;; be related to the BarBazUtil upgrade. (xz 13-1-31) (baz));; bad(defnsome-fun [];; FIXME: This has crashed occasionally since v1.2.3. It may;; be related to the BarBazUtil upgrade. (xz 13-1-31) (baz))
Tag the annotation with your initials and a date so its relevance canbe easily verified.
(defnsome-fun [];; FIXME: This has crashed occasionally since v1.2.3. It may;; be related to the BarBazUtil upgrade. (xz 13-1-31) (baz))
In cases where the problem is so obvious that any documentation wouldbe redundant, annotations may be left at the end of the offending linewith no note. This usage should be the exception and not the rule.
(defnbar [] (sleep100)); OPTIMIZE
UseTODO
to note missing features or functionality that should beadded at a later date.
UseFIXME
to note broken code that needs to be fixed.
UseOPTIMIZE
to note slow or inefficient code that may causeperformance problems.
UseHACK
to note "code smells" where questionable coding practiceswere used and should be refactored away.
UseREVIEW
to note anything that should be looked at to confirm itis working as intended. For example:REVIEW: Are we sure this is how theclient does X currently?
Use other custom annotation keywords if it feels appropriate, but besure to document them in your project’sREADME
or similar.
Docstrings are the primary way to document Clojure code. Many definition forms(e.g.def
,defn
,defmacro
,ns
)support docstrings and usually it’s a good idea to make good use of them, regardlessof whether the var in question is something public or private.
If a definition form doesn’t support docstrings directly you can still supply them viathe:doc
metadata attribute.
This section outlines some of the common conventions and bestpractices for documenting Clojure code.
If a form supports docstrings directly prefer them over using:doc
metadata:
;; good(defnfoo"This function doesn't do much." []...)(nsfoo.bar.core"That's an awesome library.");; bad(defnfoo ^{:doc"This function doesn't do much."} []...)(ns ^{:doc"That's an awesome library.") foo.bar.core)
Let the first line in the docstring be a complete, capitalizedsentence which concisely describes the var in question. This makes iteasy for tooling (Clojure editors and IDEs) to display a short a summary ofthe docstring at various places.
;; good(defnfrobnitz"This function does a frobnitz. It will do gnorwatz to achieve this, but only under certain circumstances." []...);; bad(defnfrobnitz"This function does a frobnitz. It will do gnorwatz to achieve this, but only under certain circumstances." []...)
Important tools such ascljdoc support Markdown in docstrings so leverage it for nicely formatted documentation.
;; good(defnqzuf-number"Computes the [Qzuf number](https://wikipedia.org/qzuf) of the `coll`. Supported options in `opts`: | key | description | | --------------|-------------| | `:finite-uni?`| Assume finite universe; default: `false` | `:complex?` | If OK to return a [complex number](https://en.wikipedia.org/wiki/Complex_number); default: `false` | `:timeout` | Throw an exception if the computation doesn't finish within `:timeout` milliseconds; default: `nil` Example: ```clojure (when (neg? (qzuf-number [1 2 3] {:finite-uni? true})) (throw (RuntimeException.\"Error in the Universe!\"))) ```" [coll opts]...)
Document all positional arguments, and wrap them them with backticks(`) so that editors and IDEs can identify them and potentially provide extrafunctionality for them.
;; good(defnwatsitz"Watsitz takes a `frob` and converts it to a znoot. When the `frob` is negative, the znoot becomes angry." [frob]...);; bad(defnwatsitz"Watsitz takes a frob and converts it to a znoot. When the frob is negative, the znoot becomes angry." [frob]...)
Wrap any var references in the docstring with ` so that toolingcan identify them. Wrap them with[[..]]
if you want to link to them.
;; good(defnwombat"Acts much like `clojure.core/identity` except when it doesn't. Takes `x` as an argument and returns that. If it feels like it. See also [[kangaroo]]." [x]...);; bad(defnwombat"Acts much like clojure.core/identity except when it doesn't. Takes `x` as an argument and returns that. If it feels like it. See also kangaroo." [x]...)
Docstrings should be composed of well-formed English sentences. Every sentenceshould start with a capitalized word, be grammatically coherent, and endwith appropriate punctuation. Sentences should be separated with a single space.
;; good(deffoo"All sentences should end with a period (or maybe an exclamation mark). The sentence should be followed by a space, unless it concludes the docstring.");; bad(deffoo"all sentences should end with a period (or maybe an exclamation mark). The sentence should be followed by a space, unless it concludes the docstring.")
Indent multi-line docstrings by two spaces.
;; good(nsmy.ns"It is actually possible to document a ns. It's a nice place to describe the purpose of the namespace and maybe even the overall conventions used. Note how _not_ indenting the docstring makes it easier for tooling to display it correctly.");; bad(nsmy.ns"It is actually possible to document a ns.It's a nice place to describe the purpose of the namespace and maybe eventhe overall conventions used. Note how _not_ indenting the docstring makesit easier for tooling to display it correctly.")
Neither start nor end your docstrings with any whitespace.
;; good(deffoo"I'm so awesome."42);; bad(defsilly" It's just silly to start a docstring with spaces. Just as silly as it is to end it with a bunch of them."42)
When adding a docstring — especially to a function using the above form — takecare to correctly place the docstring after the function name, not after theargument vector. The latter is not invalid syntax and won’t cause an error,but includes the string as a form in the function body without attaching it tothe var as documentation.
;; good(defnfoo"docstring" [x] (bar x));; bad(defnfoo [x]"docstring" (bar x))
Note | Place docstrings for (defprotocolMyProtocol"MyProtocol docstring" (foo [this x y z]"foo docstring") (bar [this]"bar docstring")) |
Store your tests in a separate directory, typicallytest/yourproject/
(asopposed tosrc/yourproject/
). Your build tool is responsible for makingthem available in the contexts where they are necessary; most templateswill do this for you automatically.
Name your nsyourproject.something-test
, a file which usually lives intest/yourproject/something_test.clj
(or.cljc
,cljs
).
When usingclojure.test
, define your testswithdeftest
and name themsomething-test
.
;; good(deftestsomething-test...);; bad(deftestsomething-tests...)(deftesttest-something...)(deftestsomething...)
If you are publishing libraries to be used by others, make sure tofollow theCentral Repositoryguidelinesfor choosing yourgroupId
andartifactId
. This helps to preventname conflicts and facilitates the widest possible use. A goodexample isComponent - itscoordinates arecom.stuartsierra/component
.
Another approach that’s popular in the wild is to use a project (or organization)name as thegroupId
instead of domain name. Examples of such naming would be:
cider/cider-nrepl
nrepl/nrepl
nrepl/drawbridge
clj-commons/fs
Avoid unnecessary dependencies. For example, a three-line utilityfunction copied into a project is usually better than a dependencythat drags in hundreds of vars you do not plan to use.
Code in a functional way, using mutation only when it makes sense.
Be consistent. In an ideal world, be consistent with these guidelines.
Use common sense.
One problem with style guides is that it’s often hard to remember all the guidelinesand to apply them consistently. We’re only humans, after all. Fortunately,there are a bunch of tools that can do most of the work for us.
Tip | It’s a great idea run such tools as part of your continuous integration (CI).This ensure that all the code in one project is consistent with the style you’reaiming for. |
There are some lint tools created by the Clojure community that might aid youin your endeavor to write idiomatic Clojure code.
kibit is a static code analyzer forClojure which usescore.logic tosearch for patterns of code for which there might exist a more idiomaticfunction or macro.
clj-kondo is a linter that detectsa wide number of discouraged patterns and suggests improvements, based on thisstyle guide.
While most Clojure editors and IDEs can format the code, according to the layout guidelinesoutlined here, it’s always handy to have some command-line code formatting tools. There area couple of options for Clojure that do a great job when it comes to formatting the codeas suggested in this guide:
Note | When it comes to editors - Emacs’sclojure-mode by default will format the code exactly as outlined in the guide.Other editors might require some configuration tweaking to produce the same results. |
This guide was started in 2013 byBozhidar Batsov, following thesuccess of asimilar project he had created in the Ruby community.
Bozhidar was very passionate about both Clojure and good programming style and he wanted to bridge the gap between what wascovered by theClojure library coding guidelines and what the style guides for languages like Java, Python and Ruby would typically cover.Bozhidar still serves as the guide’s primary editor, but there’s an entire editor team supporting the project.
Since the inception of the guide we’ve received a lot of feedback from members of the exceptional Clojure community around the world.Thanks for all the suggestions and the support! Together we can make a resource beneficial to each and every Clojure developer out there.
Many people, books, presentations, articles and other style guides influenced the community Clojure style guide. Here are some of them:
The Clojure style guide is stewarded by an editor team of experiencedClojurists that aims to reduce all the input we get (e.g. feedback andsuggestions) to a better reference for everyone.
The guide is still a work in progress - some guidelines are lacking examples, some guidelines don’t have examples that illustrate them clearly enough.Improving such guidelines is a great (and simple way) to help the Clojure community!
In due time these issues will (hopefully) be addressed - just keep them in mind for now.
Nothing written in this guide is set in stone.It’s my desire to work together with everyone interested in Clojure coding style, so that we could ultimately create a resource that will be beneficial to the entire Clojure community.
Feel free to open tickets or send pull requests with improvements.Thanks in advance for your help!
You can also support the style guide (and all my Clojure projects likeCIDER, nREPL, orchard, etc) with financial contributions via one of the following platforms:
It’s easy, just follow the contribution guidelines below:
Forkbbatsov/clojure-style-guide on GitHub
Make your feature addition or bug fix in a feature branch.
Include agood description of your changes
Push your feature branch to GitHub
Send aPull Request
This guide is written inAsciiDoc and is published as HTML usingAsciiDoctor.The HTML version of the guide is hosted on GitHub Pages.
Originally the guide was written in Markdown, but was converted to AsciiDoc in 2019.
This work is licensed under aCreative Commons Attribution 3.0 Unported License
A community-driven style guide is of little use to a community thatdoesn’t know about its existence. Tweet about the guide, share it withyour friends and colleagues. Every comment, suggestion or opinion weget makes the guide just a little bit better. And we want to have thebest possible guide, don’t we?
ns
macro, but it’s extremely unlikely you’ll ever need it in the body of a function.About
A community coding style guide for the Clojure programming language
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.