Stop Being So Negative
My brain hurts and I cringe a bit whenever I stumble across an unnecessarily complicated negative condition. Also, to be perfectly clear and honest, I never have written anything that remotely resembles any of these examples. Why would I say I've written terrible code when I've never written terrible code?
Let me show you what I mean:
# Is the collection NOT empty?
if !collection.empty?
or
# Is the thing NOT nil?
if !thing.nil?
Worse still is when a negative condition is paired with an unless
.
# UNLESS the collection is NOT empty, do the next thing.
unless !collection.empty?
Or the universally agreed upon most heinous double negative in Ruby:
# UNLESS the thing is NOT nil, do the next thing.
unless !thing.nil?
The point here is that for some reason negative logic always seems tougher to understand than its positively phrased counterpart.
Ruby is too Expressive to Warrant the use of Negatives.
The human brain evolved hundreds of thousands of years ago to be good at a great many things, but tracking negative logic is not one of them. At least that's the case for my particular human brain.
Ruby is so expressive, we should never have to write tough to read nonsense code like if !collection.nil?
.
Instead we should be writing our conditions with a positive voice.
A collection not being empty is the same as saying a collection with at least one element. A thing not being nothing is the same as saying a thing that exists.
Bad
if !collection.empty?
Good
# present is a rails-ism
if collection.present?
What if we are not using Rails? Well Ruby has some non-negative tools for us as well!
irb(main):001:1* collection = []
=> []
# Is there at least one element in our collection that is not
# `nil` or `false`? In other, clearer, words are any of the
# elements in our collection truthy?
irb(main):002:0> collection.any?
=> false
If our goal is to run some code when a collection is not empty, any?
is a pretty good choice.
irb(main):001:0> collection = ['hello, world!']
=> ["hello, world!"]
irb(main):002:0> collection.any?
=> true
There can be some weirdness, however.
irb(main):006:0> collection = [nil]
=> [nil]
irb(main):007:0> collection.any?
=> false
I think present?
in Rails behaves mostly how I expect it to, so that ends up being the tool I reach for most often especially because I am almost always writing Ruby in a Rails context.
[1] pry(main)> collection = []
=> []
[2] pry(main)> collection.present?
=> false
[3] pry(main)> collection = ['hello, world!']
=> ["hello, world!"]
[4] pry(main)> collection.present?
=> true
# Careful now. But, this kind of makes sense. There is something
# in the collection after all. That something just happens to be nothing.
[5] pry(main)> collection = [nil]
=> [nil]
[6] pry(main)> collection.present?
=> true
# :mindblown:
Let's look at our nil?
negative check. What are we really saying when we ask is the thing
NOT nil?
? We are really asking, does thing
exist?
Bad
if !thing.nil?
Good
if thing
If thing can be nil
, true
, or false
and we only want to proceed if thing
is true
or false
, we should try:
# Again, stick to the positives.
if [true, false].include? thing
In my experience, this could be filed in the edge case category as typically checking for falsey values is good enough.
Acceptable uses for Unless
There is only one acceptable use for unless and that is as a guard statement at the very start of a method. It may not use any compound, multi-clause logic. Nor may it be placed anywhere but the very first line of a method because we do not hate our colleagues.
Bad
unless condition
...
end
Also Bad
def foo
# some code
...
unless something_happened_in_the_above_code
end
end
The Worst
# This is an if
unless !condition
...
end
Good
def foo
return unless condition
# mandatory empty line as a sign of humility.
...
# remaining implementation that should only run if the condition is truthy.
end
Let's make our code bases more positive places for all involved. Keep the negativity to a minimum.
Top comments (0)