| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
assoc-ing an update with the same key are hard to read. update is known and
idiomatic.
; bad
(assoc coll :a (+ (:a coll) 5))
(assoc coll :a (+ (coll :a) 5))
(assoc coll :a (+ (get coll :a) 5))
; good
(update coll :a + 5)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 1.0 | 1.0 |
A macro that nests an unquote-splicing in a macro with a & body can lead
to subtle hard to debug errors. Better to wrap the unquote-splicing in
a do to force it into 'expression position'.
# bad
`(binding [max mymax] ~@body)
# good
`(binding [max mymax] (let [res# (do ~@body)] res#))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Checks for (/ x 1).
; bad
(/ x 1)
; good
x
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 1.2.1 |
map is lazy, which carries a performance and memory cost. dorun uses seq iteration to realize the entire sequence, returning nil. This style of iteration also carries a performance and memory cost. dorun is intended for more complex sequences, whereas a simple map can be accomplished with reduce + conj.
run! uses reduce which non-lazy and has no seq overhead.
; bad
(dorun (map println (range 10)))
; good
(run! println (range 10))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Using the Obj/staticMethod form maps the method call to Clojure's natural function
position.
; bad
(. Obj staticMethod args)
; good
(Obj/staticMethod args)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Using the .method form maps the method call to Clojure's natural function position.
; bad
(. obj method args)
; good
(.method obj args)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1.119 | 0.1.119 |
deftype and defrecord will throw errors if you define multiple fields
with the same name, but it's good to catch these things early too.
# bad
(defrecord Foo [a b a])
# good
(defrecord Foo [a b c])
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Avoid wrapping functions in pass-through anonymous function defitions.
; bad
(fn [num] (even? num))
; good
even?
; bad
(let [f (fn [num] (even? num))] ...)
; good
(let [f even?] ...)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Idiomatic if defines both branches. when returns nil in the else branch.
; bad
(if (some-func) :a nil)
; good
(when (some-func) :a)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Idiomatic if-let defines both branches. when-let returns nil in the else branch.
; bad
(if-let [a 1] a nil)
; good
(when-let [a 1] a)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Idiomatic if defines both branches. when-not returns nil in the truthy branch.
; bad
(if (some-func) nil :a)
; good
(when-not (some-func) :a)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
if-not exists, so use it.
; bad
(if (not x) y z)
; good
(if-not x y z)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
when-not already defines an implicit do. Rely on it.
; bad
(if-not x (do (println :a) (println :b) :c))
; good
(if-not x (println :a) (println :b) :c)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Two nots cancel each other out.
; bad
(if-not (not x) y z)
; good
(if x y z)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
or exists so use it lol.
; bad
(if x x y)
; good
(or x y)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
vec and set are succinct and meaningful.
; bad
(into [] coll)
; good
(vec coll)
; bad
(into #{} coll)
; good
(set coll)
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1.69 | 0.1.69 |
if-let exists so use it. Suggestions can be wrong as there's no code-walking to
determine if result binding is used in falsy branch.
; bad
(let [result (some-func)] (if result (do-stuff result) (other-stuff)))
; good
(if-let [result (some-func)] (do-stuff result) (other-stuff))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1.69 | 0.1.69 |
when-let exists so use it.
; bad
(let [result (some-func)] (when result (do-stuff result)))
; good
(when-let [result (some-func)] (do-stuff result))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
loop has an implicit do. Use it.
; bad
(loop [] (do (println 1) (println 2)))
; good
(loop [] (println 1) (println 2))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
Empty loops with nested when can be while.
; bad
(loop [] (when (some-func) (println 1) (println 2) (recur)))
; good
(while (some-func) (println 1) (println 2) (recur))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1.69 | 0.1.69 |
when calls should have at least 1 expression after the condition.
; bad
(when true)
(when (some-func))
; good
(when true (do-stuff))
(when (some-func) (do-stuff))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 1.2.0 |
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.
; bad
(not (empty? coll))
; good (chosen style :seq (default))
(seq coll)
; good (chosen style :not-empty)
(not-empty coll)
| Name | Default | Options |
|---|---|---|
:chosen-style | :seq | :seq, :not-empty |
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 1.3.0 | 1.3.0 |
In the ns form prefer :require :as over :require :refer over :require :refer :all. Prefer :require over :use; the latter form should be considered deprecated for new code.
# bad
(ns examples.ns
(:use clojure.zip))
# good
(ns examples.ns
(:require [clojure.zip :as zip]))
(ns examples.ns
(:require [clojure.zip :refer [lefts rights]]))
(ns examples.ns
(:require [clojure.zip :refer :all]))
| Name | Default | Options |
|---|---|---|
:chosen-style | :as | :as, :refer, :all |
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
A number of core functions take any number of arguments and return the arg if given only one. These calls are effectively no-ops, redundant, so they should be avoided.
Current list of clojure.core functions this linter checks:
->, ->>cond->, cond->>some->, some->>comp, partial, merge; bad
(-> x)
(->> x)
(cond-> x)
(cond->> x)
(some-> x)
(some->> x)
(comp x)
(partial x)
(merge x)
; good
x
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 0.1 |
repeatedly has an arity for limiting the number of repeats with take.
; bad
(take 5 (repeatedly (range 10))
; good
(repeatedly 5 (range 10))
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 0.1 | 1.2.1 |
Threading macros require more effort to understand so only use them with multiple args to help with readability.
; bad
(-> x y)
(->> x y)
; good
(y x)
; bad
(-> x (y z))
; good
(y x z)
; bad
(->> x (y z))
; good
(y z x)
| Name | Default | Options |
|---|---|---|
:chosen-style | :inline | :inline, :avoid-collections |
| Enabled by default | Version Added | Version Updated |
|---|---|---|
| true | 1.0 | 1.0 |
A macro that wraps a splicing unquote in a try-catch or try-finally can lead
to subtle hard to debug errors. Better to wrap the splicing unquote in a do
to force it into 'expression position'.
# bad
`(try ~@body (finally :true))
# good
`(try (do ~@body) (finally :true))
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 |