Do you find unless to be confusing? I know I do. Especially if it’s combined with one or more boolean operators.
Take this example.
def validation
"Bingo!" unless email_invalid? || email_missing?
end
Do you find that easy to read? I know I don’t.
Figuring out boolean operator precedence is hard enough, but throwing unless in the mix is just too much for my brain to process.
To me, the following feels much easier to understand.
def validation
"Bingo!" if email_present? && email_valid?
end
There’s something about if
that makes it easier to read and understand.
Unless vs. if
unless
is the exact opposite of if
. It’s a negated if
.
In other words, the following three examples are equivalent.
unless number.even?
# runs if `number` is NOT even
else
# runs if `number` is even
end
if not number.even?
# runs if `number` is NOT even
else
# runs if `number` is even
end
if !number.even?
# runs if `number` is NOT even
else
# runs if `number` is even
end
You won’t see unless...else
used very often in real life code because you can always replace it with an if...else
statement.
unless true
# do falsy stuff
else
# do truthy stuff
end
Can be written as an if...else
statement. Like so.
if true
# do truthy stuff
else
# do falsy stuff
end
How to use unless
A good use for unless is when you want to check something at the beginning of a method. Also known as a guard clause.
def my_method(name)
return unless name.empty?
# ...
end
I find that using the modifier version of unless
, as a guard clause improves readability.
What is the value of the if/unless modifier?
If you’re expecting a return value from the modifier, you might be surprised to find that there isn’t one. Let us test this.
What does the following method return if name is empty?
def some_method(name)
"Bingo!" unless name.empty?
end
some_method("") # => nil
Likewise, what does the following if
expression evaluate to when number is 0?
def some_method(number)
"Bingo!" if number > 0
end
some_method(0) # => nil
As you can see, both of them are nil
. But why is that?
Two reasons:
- It’s because the expression never runs.
- An empty code block returns
nil
.
So if there is no code to run, the entire block (i.e. the method) returns nil.
Check out these other empty code blocks.
class Foo; end # => nil
begin; end # => nil
eval("") # => nil