Enabled by default | Version Added | Version Updated |
---|---|---|
true | 0.1 | 0.1 |
assoc
-ing an update with the same key is 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
(when-not x (println :a) (println :b) :c)
Enabled by default | Version Added | Version Updated |
---|---|---|
true | 0.1 | 0.1 |
Two not
s 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
min
, max
, distinct?
; bad
(-> x)
(->> x)
(cond-> x)
(cond->> x)
(some-> x)
(some->> x)
(comp x)
(partial x)
(merge x)
(min x)
(max x)
(distinct? 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))
Enabled by default | Version Added | Version Updated |
---|---|---|
true | 1.11 | 1.11 |
Due to munging rules, underscores in namespaces can confuse tools and libraries which expect that underscores in class names should be dashes in Clojure.
# bad
(ns foo_bar.baz_qux)
# good
(ns foo-bar.baz-qux)
Enabled by default | Version Added | Version Updated |
---|---|---|
false | 1.8.0 | 1.8.0 |
Because we can't (or won't) check for interop, *warn-on-reflection*
should
be at the top of every file out of caution.
# bad
(ns foo.bar)
(defn baz [a b] (+ a b))
# good
(ns foo.bar)
(set! *warn-on-reflection* true)
(defn baz [a b] (+ a b))
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close