Shortcuts with Conditionals
Conditional Operators
You’re probably familiar with and
and or
in Clojure. If not, these are
basically the same as the &&
and ||
operators in any other
language–unless you’re using VB (I’m sorry).
So in Clojure, I can do something like this and the operator will work the same as you’d expect in any other language.
user=> (and true true true false true)
false
user=> (or false false false true false)
true
Everything is a Boolean
One cool thing about Clojure is that just about everything evaluates to a boolean! So I could modify the conditionals like so…
user=> (and [1 2 3] "a" :b \C (fn [a b] (+ a b)) {} 5.2)
5.2
user=> (or [1 2 3] "a" :b \C (fn [a b] (+ a b)) {} 5.2)
[1 2 3]
Why does Clojure do this? Well, I couldn’t tell you WHY they decided to
make it work this way, but in the documentation for and
and or
:
and
returns the last truthy value or the first falsey valueor
returns the first truthy value or the last falsey value
Since everything in the and
form evaluates to true, then we just got back
the last value in the form. Similarly in the or
form, since the first item
evaluates to true, we received that value back.
Refactoring Conditionals
Why does it matter that we get values back from conditional operators? Well, coming from an OO background I’m used to writing stuff like this:
if (a != null)
return a;
else if (b != null)
return b;
else if ...
So when I need to write a similar function in Clojure, I’m immediately thinking of structures like this:
(defn do-something [a b c]
(cond
a a
b b
c c
:else nil))
This looks silly. We’re literally returning one of the values which evaluate
to true. Then if every value results in false, we just return nil
anyways!
Now this is a function I absolutely need in my application–everything will fall apart otherwise. How can this be improved?
Recall that or
will return the first truthy value or the last falsey value.
Well, do-something
looks a lot like it’s trying to mimic that sort of logic.
Let’s refactor it to use or
instead and see what we get.
(defn do-something [a b c]
(or a b c nil))
Well hold on now–do-something
isn’t actually doing anything! It’s just
calling or on the same operators (plus nil
) it’s given! We may as well
just remove do-something
altogether and put its body in the parent function.
(defn parent [a b c]
; ...
(or a b c nil))