Reputation: 8854
The docstring for empty?
says "Please use the idiom (seq x) rather than (not (empty? x))". MisterMetaphor points out that using seq
as a predicate can make sense when used in if-let
:
(if-let [s (seq might-be-empty)]
(fn-for-non-empty-seq s)
(fn-for-empty-seq))
Should I really use seq
to test for non-emptiness in general, though? seq
may convert its argument to a different form. For example:
user=> (class (lazy-seq '(1 2 3)))
clojure.lang.LazySeq
user=> (class (seq (lazy-seq '(1 2 3))))
clojure.lang.PersistentList
user=> (class (map inc [1 2 3]))
clojure.lang.LazySeq
user=> (class (seq (map inc [1 2 3])))
clojure.lang.ChunkedCons
That seems like a waste of cycles if just want to test for non-emptiness, and don't need a new binding, or if I don't need the conversion before the binding. Wouldn't not-empty
be a better choice in such cases? It returns nil
if its argument is empty, and its argument unchanged if non-empty.
(if (not-empty [])
"more to do"
"all done")
Upvotes: 11
Views: 3117
Reputation: 26446
First, check out the definition of empty?
:
(defn empty?
"Returns true if coll has no items - same as (not (seq coll)).
Please use the idiom (seq x) rather than (not (empty? x))"
{:added "1.0"
:static true}
[coll] (not (seq coll)))
So empty?
is (not (seq coll))
. That's why it's discouraged to complement empty?
, because you are doing a double negation on seq
.
Now see not-empty
(defn not-empty
"If coll is empty, returns nil, else coll"
{:added "1.0"
:static true}
[coll] (when (seq coll) coll))
Surprise, it uses seq
as the test of non-emptiness too. This not-empty
is useful if the type matters -- nth
performance on vectors, conj
behavior, etc. -- because it does return non-empty colls as is. Used as just a predicate though, it would only be wrapping seq
.
Don't worry about seq
being heavy-handed though. It does little more than return an iterator. It does not actually convert the entire data structure.
Upvotes: 25