Clojure's "zero?" has some quirks
In short, Clojure’s zero?
isn’t exactly like nil?
and true?
and others. It checks different numeric types and throws if given a non-numeric type.
This post was written about Clojure 1.8.0.
Clojure has a few functions that do equality checks, like nil?
, true?
, false?
, and zero?
. The docs all look about the same for these functions. nil?
“returns true if x is nil, false otherwise” and the rest look similar.
But despite similar documentation, zero?
is a little bit different from the rest.
Let’s start by looking at true?
. It accepts anything as an argument; a boolean, a string, whatever.
(true? true) ;; true
(true? false) ;; false
(true? "str") ;; false
In contrast, zero?
throws an error if the argument isn’t a number.
(zero? 0) ;; true
(zero? 42.0) ;; false
(zero? "str") ;; throws java.lang.ClassCastException
This was a little surprising to me! It’s not documented and it also doesn’t match the other equality checkers.
zero?
is also a little smarter about different numeric types. zero?
handles cases that a simple equality check doesn’t.
(= 0 0) ;; true
(= 0 0.0) ;; false
(= 0 "str") ;; false
(zero? 0) ;; true
(zero? 0.0) ;; true
(zero? "0") ;; throws java.lang.ClassCastException
You could write a version of zero?
that works like nil?
, which won’t throw on non-numeric input but will still work for various number types.
(defn safe-zero? [x]
(and (number? x) (zero? x)))
(safe-zero? 0) ;; true
(safe-zero? 0.0) ;; true
(safe-zero? "str") ;; false
zero?
isn’t vastly more complicated than I expected, but it’s a little more nuanced than I thought at first glance.