| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Check for round-about clojure.string/join.
; avoid
(apply str x)
; prefer
(clojure.string/join x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Check for round-about clojure.string/join.
; avoid
(apply str (interpose "," x))
; prefer
(clojure.string/join "," x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Check for round-about clojure.string/reverse.
; avoid
(apply str (reverse x))
; prefer
(clojure.string/reverse x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Layering assoc calls are hard to read. assoc-in is known and idiomatic.
; avoid
(assoc coll :key1 (assoc (:key2 coll) :key2 new-val))
(assoc coll :key1 (assoc (coll :key2) :key2 new-val))
(assoc coll :key1 (assoc (get coll :key2) :key2 new-val))
; prefer
(assoc-in coll [:key1 :key2] new-val)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 0.1.85 |
It's nice when the default branch is consistent.
; avoid
(cond
(< 10 num) (println 10)
(< 5 num) (println 5)
true (println 0))
; prefer
(cond
(< 10 num) (println 10)
(< 5 num) (println 5)
:else (println 0))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
vector is succinct and meaningful.
; avoid
(conj [] :a b {:c 1})
; prefer
(vector :a b {:c 1})
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.0 | 1.19.0 |
(defn []) is preferable over (def (fn [])). Extrapolate to closures too.
; avoid
(def check-inclusion
(let [allowed #{:a :b :c}]
(fn [i] (contains? allowed i))))
; prefer
(let [allowed #{:a :b :c}]
(defn check-inclusion [i]
(contains? allowed i)))
; avoid
(def some-func
(fn [i] (+ i 100)))
; prefer
(defn some-func [i]
(+ i 100))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
false? exists so use it.
; avoid
(= false x)
(= x false)
; prefer
(false? x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
nil? exists so use it.
; avoid
(= nil x)
(= x nil)
; prefer
(nil? x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
true? exists so use it.
; avoid
(= true x)
(= x true)
; prefer
(true? x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
zero? exists so use it.
; avoid
(= 0 num)
(= num 0)
(== 0 num)
(== num 0)
; prefer
(zero? num)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Check for (filter (complement pred) coll).
; avoid
(filter (complement even?) coll)
; prefer
(remove even? coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
filterv is preferable for using transients.
; avoid
(vec (filter pred coll))
; prefer
(filterv pred coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
ffirst is succinct and meaningful.
; avoid
(first (first coll))
; prefer
(ffirst coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
fnext is succinct and meaningful.
; avoid
(first (next coll))
; prefer
(fnext coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.15.0 | 1.18.0 |
clojure.test/is expects =-based assertions to put the expected value first.
This rule uses two checks on the = call to determine if it should issue a diagnostic:
; avoid
(is (= status 200))
(is (= (my-plus 1 2) 3))
; prefer
(is (= 200 status))
(is (= 3 (my-plus 1 2)))
; non-issues
(is (= (hash-map :a 1) {:a 1}))
(is (= (hash-set :a 1) #{:a 1}))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
let has an implicit do, so use it.
; avoid
(let [a 1 b 2] (do (println a) (println b)))
; prefer
(let [a 1 b 2] (println a) (println b))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Check for (apply concat (apply map x y)).
; avoid
(apply concat (apply map x y))
; prefer
(mapcat x y)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Check for (apply concat (map x y z)).
; avoid
(apply concat (map x y))
(apply concat (map x y z))
; prefer
(mapcat x y)
(mapcat x y z)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for simple -1 that should use clojure.core/dec.
; avoid
(- x 1)
; prefer
(dec x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for x - 0.
; avoid
(- x 0)
; prefer
x
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.3.0 | 1.3.0 |
Sort the arities of a function from fewest to most arguments.
; avoid
(defn foo
([x] (foo x 1))
([x y & more] (reduce foo (+ x y) more))
([x y] (+ x y)))
; prefer
(defn foo
([x] (foo x 1))
([x y] (+ x y))
([x y & more] (reduce foo (+ x y) more)))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for (* x 1).
; avoid
(* x 1)
(* 1 x)
; prefer
x
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for (* x 0).
; avoid
(* x 0)
(* 0 x)
; prefer
0
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
neg? exists so use it.
; avoid
(< num 0)
(> 0 num)
; prefer
(neg? num)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for simple nested additions.
; avoid
(+ x (+ y z))
(+ x (+ y z a))
; prefer
(+ x y z)
(+ x y z a)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for simple nested multiply.
; avoid
(* x (* y z))
(* x (* y z a))
; prefer
(* x y z)
(* x y z a)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1.69 | 1.15.2 |
new special form is discouraged for dot usage.
NOTE: The style :method-value requires Clojure version 1.12+.
; avoid
(new java.util.ArrayList 100)
; prefer (chosen style :dot (default))
(java.util.ArrayList. 100)
; avoid (chosen style :method-value)
(java.util.ArrayList. 100)
; prefer (chosen style :method-value)
(java.util.ArrayList/new 100)
| Name | Default | Options |
|---|---|---|
:chosen-style | :dot | :dot, :method-value |
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
nfirst is succinct and meaningful.
; avoid
(next (first coll))
; prefer
(nfirst coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
nnext is succinct and meaningful.
; avoid
(next (next coll))
; prefer
(nnext coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
not= exists, so use it.
; avoid
(not (= num1 num2))
; prefer
(not= num1 num2)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
some? exists so use it.
; avoid
(not (nil? x))
; prefer
(some? x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
not-any? is succinct and meaningful.
; avoid
(not (some even? coll))
; prefer
(not-any? even? coll)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for simple +1 that should use clojure.core/inc.
; avoid
(+ x 1)
(+ 1 x)
; prefer
(inc x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Checks for x + 0.
; avoid
(+ x 0)
(+ 0 x)
; prefer
x
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
pos? exists so use it.
; avoid
(< 0 num)
(> num 0)
; prefer
(pos? num)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1.69 | 0.1.69 |
Use boolean if you must return true or false from an expression.
; avoid
(if some-val true false)
(if (some-func) true false)
; prefer
(boolean some-val)
(boolean (some-func))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | false | 0.1.69 | 0.1.69 |
NOTE: Requires Clojure version 1.11.0.
Prefer clojure.math to interop.
; avoid
Math/PI
(Math/atan 45)
; prefer
clojure.math/PI
(clojure.math/atan 45)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.9.0 | 1.9.0 |
Prefer clojure.string to interop.
| method | clojure.string |
|---|---|
.contains | clojure.string/includes? |
.endsWith | clojure.string/ends-with? |
.replace | clojure.string/replace |
.split | clojure.string/split |
.startsWith | clojure.string/starts-with? |
.toLowerCase | clojure.string/lower-case |
.toUpperCase | clojure.string/upper-case |
.trim | clojure.string/trim |
; avoid
(.toUpperCase "hello world")
; prefer
(clojure.string/upper-case "hello world")
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | false | false | 0.1.69 | 1.0 |
cond checking against the same value in every branch is a code smell.
This rule uses the first test-expr as the template to compare against each other test-expr. It has a number of conditions it checks as it runs:
cond is well-formed (aka even number of args).cond has more than 1 pair.true or a keyword.Provided all of that is true, then the middle arguments of the test-exprs are gathered and rendered into a condp.
It's possible that the check isn't written correctly, so be wary of the suggested output.
; avoid
(cond
(= 1 x) :one
(= 2 x) :two
(= 3 x) :three
(= 4 x) :four)
; prefer
(condp = x
1 :one
2 :two
3 :three
4 :four)
; avoid
(cond
(= 1 x) :one
(= 2 x) :two
(= 3 x) :three
:else :big)
; prefer
(condp = x
1 :one
2 :two
3 :three
:big)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.15.0 | 1.17.0 |
The core builder functions are helpful when creating an object from an opaque sequence, but are much less readable when used in maps to get around issues with anonymous function syntax peculiarities.
; avoid
(map #(hash-map :a 1 :b %) (range 10))
; prefer
(for [item (range 10)] {:a 1 :b item})
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 0.1.69 |
vary-meta works like swap!, so no need to access and overwrite in two steps.
; avoid
(with-meta x (assoc (meta x) :filename filename))
; prefer
(vary-meta x assoc :filename filename)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | false | 1.21.0 | 1.21.0 |
require supports prefixed libspecs, shared "parent" namespaces with subsections in separate vectors. This allows for 'DRY' libspecs but harms readability and discoverability while not actually providing a great reduction in characters.
; avoid
(ns foo.bar
(:require [clojure
[string :as str]
[set :as set]]))
(require '[clojure
[string :as str]
[set :as set]])
; prefer
(ns foo.bar
(:require [clojure.string :as str]
[clojure.set :as set]))
(require '[clojure.string :as str]
'[clojure.set :as set])
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.11 | 1.11 |
reduce calls the provided function on every element in the provided
collection. Because of how str is implemented, a new string is created
every time it's called. Better to rely on clojure.string/join's efficient
StringBuilder and collection traversal.
Additionally, the 2-arity form of reduce returns the first item without
calling str on it if it only has one item total, which is
generally not what is expected when calling str on something.
; avoid
(reduce str x)
(reduce str "" x)
; prefer
(clojure.string/join x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1.69 | 0.1.69 |
Directly nested lets can be merged into a single let block.
; avoid
(let [a 1]
(let [b 2]
(println a b)))
(let [a 1
b 2]
(println a b))
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | false | 1.19.0 | 1.19.0 |
Some clojure.core functions and macros take a variable number of args, so there's no need to nest calls. To check non-clojure.core functions, they can be added to the config under key :fn-names: style/redundant-nested-call {:fn-names [foo]}.
note
This can have performance implications in certain hot-loops.
; avoid
(+ 1 2 (+ 3 4))
(comp :foo :bar (comp :qux :ply))
; prefer
(+ 1 2 3 4)
(comp :foo :bar :qux :ply)
; with `:fn-names [foo]`
; avoid
(foo 1 2 (foo 3 4))
; prefer
(foo 1 2 3 4)
| Name | Default | Options |
|---|---|---|
:fn-names | #{+' comp * min concat or *' merge every-pred + str some-fn max lazy-cat and} | Set |
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.10.0 | 1.10.0 |
Clojure regex literals (#"") are passed to java.util.regex.Pattern/compile at read time. re-pattern checks if the given arg is a Pattern, making it a no-op when given a regex literal.
; avoid
(re-pattern #".*")
; prefer
#".*"
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| false | true | true | 0.1.119 | 1.11 |
Sets can be used as functions and they're converted to static items when they contain constants, making them fairly fast. However, they're not as fast as [[case]] and their meaning is less clear at first glance.
; avoid
(#{'a 'b 'c} elem)
; prefer
(case elem (a b c) elem nil)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1.69 | 0.1.69 |
assoc-in loops over the args, calling assoc for each key. If given a single key,
just call assoc directly instead for performance and readability improvements.
; avoid
(assoc-in coll [:k] 10)
; prefer
(assoc coll :k 10)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Convert (.toString) to (str).
; avoid
(.toString x)
; prefer
(str x)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 1.11 | 1.11 |
for is a complex and weighty macro. When simply applying a function to each element, better to rely on other built-ins.
; avoid
(for [item items]
(f item))
; prefer
(map f items)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
update-in-ing an assoc with the same key are hard to read. assoc-in is known
and idiomatic.
; avoid
(update-in coll [:a :b] assoc 5)
; prefer
(assoc-in coll [:a :b] 5)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1.69 | 1.2.0 |
A single item in a do is a no-op. However, it is sometimes necessary to wrap expressions in dos to avoid issues, so do surrounding ~@something will be skipped as well as #(do something).
; avoid
(do coll)
; prefer
coll
; skipped
(do ~@body)
#(do [%1 %2])
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1.69 | 1.2.0 |
when already defines an implicit do. Rely on it.
; avoid
(when x (do (println :a) (println :b) :c))
; prefer
(when x (println :a) (println :b) :c)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
when-not exists so use it lol.
; avoid
(when (not x) :a :b :c)
; prefer
(when-not x :a :b :c)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
when-not already defines an implicit do. Rely on it.
; avoid
(when-not x (do (println :a) (println :b) :c))
; prefer
(when-not x (println :a) (println :b) :c)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 0.1.69 |
seq returns nil when given an empty collection. empty? is implemented as
(not (seq coll)) so it's best and fastest to use seq directly.
; avoid
(when-not (empty? ?x) &&. ?y)
; prefer
(when (seq ?x) &&. ?y)
| Enabled by default | Safe | Autocorrect | Version Added | Version Updated |
|---|---|---|---|---|
| true | true | true | 0.1 | 1.2.0 |
Two nots cancel each other out.
; avoid
(when-not (not x) y z)
; prefer
(when x y z)
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |