The case for and against Clojure predicates returning nil
Last week, there was a mailing list post on the clojure-dev mailing list noting that several of the new predicates in Clojure 1.9 alphas return true or nil. The three new predicates are: qualified-keyword?
, qualified-ident?
, and qualified-symbol?
. Previously, all predicate functions in Clojure core, i.e. functions that end in a ?
have returned true/false. In this post I want to examine the pros and cons of this new behaviour.
If I’ve missed anything here, please get in contact and I’ll update the post.
Pros to some predicates returning nil
- The main argument for some predicates returning nil is that the predicate still returns a falsy value. Idiomatic Clojure code usually doesn’t need to distinguish between
nil
andfalse
. - The docstrings for
qualified-keyword?
for example says: “Return true if x is a keyword with a namespace”. It doesn’t say anything about what happens if it doesn’t have a namespace, so returningnil
orfalse
are both technically valid interpretations. - Not coercing the output from
qualified-keyword?
into a boolean is faster. On my computerqualified-keyword?
takes roughly 3.5 ns to run, and a version that returns a boolean value takes around 6.5 ns. In absolute terms they are both pretty small though.
The first two points aren’t strong arguments for returning nil, rather they argue that it doesn’t matter whether the functions return nil or false.
Cons to some predicates returning nil:
-
The biggest downside to this change is that it breaks a core convention that Clojure and the community has held around predicates, namely that any function ending in
?
returnstrue
orfalse
. This is my biggest concern about the changes. You can find this expectation in:- Programming Clojure: “A predicate is a function that returns either true or false. In Clojure, it is idiomatic to name predicates with a trailing question mark, for example
true?
,false?
,nil?
, andzero?
.” - Programming Clojure, 2nd Ed., Page 27. - Clojure’s own library coding standards: “Use ‘?’ suffix for predicates. N.B. - predicates return booleans”. That page also has the disclaimer: “Rules are made to be broken. Know the standards, but do not treat them as absolutes.”
- The community Clojure Style Guide: “The names of predicate methods (methods that return a boolean value) should end in a question mark (e.g.,
even?
).” - All of the Clojure standard library functions that end in a
?
return a boolean value. Anyone learning Clojure would be justified in assuming that all functions that end in?
return a boolean value if that’s the convention they have always seen. - A mailing list thread on predicates in the aforementioned Programming Clojure book.
- A mailing list thread about this exact question, whether all Clojure functions that end in
?
should return a boolean value. - Tutorials Point: “Predicates are functions that evaluate a condition and provide a value of either true or false.”
- StackOverflow on naming rules: “The main function naming conventions seem to be … Use
?
to indicate a predicate that returns true or false:sequential?
”
- Programming Clojure: “A predicate is a function that returns either true or false. In Clojure, it is idiomatic to name predicates with a trailing question mark, for example
-
The second biggest problem is that
qualified-keyword?
can return three values. “How is that?” you might be asking, “Didn’t you just say that the functions returntrue
ornil
?”. I thought so too. However as I was looking at the implementation of these functions, I realised thatqualified-keyword?
can return false as well, if not given a keyword. For example:user=> (qualified-keyword? ::abc) true user=> (qualified-keyword? :abc) nil user=> (qualified-keyword? 'abc) false
It is fairly common to use
group-by
orjuxt
with a predicate function to partition a collection into two groups. If you use any of the new qualified* functions, you will end up with three groups. Based on Alex Miller’s tweet, it seems like this was considered and accepted. -
If these predicates are used at the boundaries of systems or in interop with Java, then it would be easy to forget that the qualified* predicates may return nil, and end up passing null into a Java method as a parameter when you meant to pass false. You could also return false under certain input when you expected the function to only return true or nil, or return nil when you were expecting the result to be a boolean. These kinds of bugs can be very subtle, especially with Clojure’s falsy handling.
-
ClojureScript code is a lot smaller and enables more optimisations when it can guarantee that a boolean value is returned.
-
Clojure gets similar benefits from the JVM when it can mark the function as returning a Boolean.
-
Historically, Clojure has made a distinction between functions that return truthy and those that return true. There are predicate functions in core that return a truthy value like
some
andevery-pred
, but they don’t end in a?
. This change starts to dissolve that distinction.
Summary
Personally, I don’t see a strong reason to keep the current behaviour of the new qualified predicate functions, but you might see things differently. Whatever the outcome, I’m glad that in Clojure we don’t have to name all of our boolean functions with an “Eh” suffix.
Update
Alex Miller suggested to file a ticket about this behaviour, so I opened CLJ-2141. After some discussion, a patch was made to coerce the output of these new functions to a boolean.