Can someone explain the reasoning being this? Just spent 30 mins trying to figure out why my boolean method returned nil
and found out that in Ruby:
2.2.1 :001 > nil && true
=> nil
2.2.1 :002 > nil && false
=> nil
Since nil
is a falsey value, I would have expected the output of nil && true
to be false. Also it seems to go against the idea that conditional operators should return a boolean value.
What is the rationale behind this?
It makes sense that the boolean operator is not commutative:
nil && false != false && nil
For others seeing this, my issue was that in rails I had a statement like:
def some_method?
object.attr && object.attr > something
end
But when object.attr
is nil, the function will be nil. Which is fine in most cases but when chaining boolean methods together, not so much. I just changed it to this instead:
def some_method?
object.attr.present? && object.attr > something
end
I could do the same thing in vanilla Ruby with:
def some_method?
!!object.attr && object.attr > something
end
It's good to have an understanding of variables/values "truthiness":
First, looking at Ruby's idea of true and false:
A shorter form of that
if
/else
uses the ternary:Continuing with that:
Notice that
nil
, when used as a conditional acts likefalse
, whereas0
is true. (Perl treatsfalse
and0
as false values in tests which is a confusing point when moving to Ruby.)1
is true, and basically, in Ruby, onlynil
andfalse
are false and everything else is true.!
performs a "not", reversing the truthiness of the value:And
!!
"nots" the value twice, which we use as a way to quickly convert something from a non-true/false value to a true/false:Knowing how these work in Ruby makes it easier to understand other people's code as you'll see
!
and!!
often.All that leads back to using
&&
and||
. Using||
("or"), only one side has to betrue
to return atrue
result:With
&&
("and") both sides have to betrue
to get atrue
result:Don't confuse
||
andor
, and&&
andand
. Both!!
andor
do similar things, and&&
andand
, however their tests will occur at different times in an expression. That's order-of-precedence and is very important to know but is a different question. You can find plenty of information about that with simple searches.The statement goes through the conditions in order, will stop when a falsy result is obtained and return the value of the last evaluation performed.
In contrary to
&&
which stops at a falsy value,||
will stop at a truthy value instead.If first operand is falsy (
nil
orfalse
), then, second operand of&&
will not be evaluated, asfalsy && whatever
will be alwaysfalsy
Problem
You list two related problems:
nil
instead offalse
.nil
,false
, or some other object that doesn't respond to the next method in the chain.Nil And False
In your examples, you are chaining methods that may sometimes be called on
nil
. In Ruby, nil is a falsey value (e.g. it isn't "true"), but it is not actually false, either. Consider:They aren't even descended from the same class.
It is often best to think of nil as a special value meaning "undefined" or "not applicable". It may also be useful to think of nil as similar to the special database value of null. Nil (like null) isn't true, false, or empty, and understanding this will help you avoid many confusing exceptions.
Short-Circuit Evaluation
When you evaluate
nil && false
you're really evaluating two separate expressions:The expression
nil
is not true, and therefore short-circuits and never evaluates the expressionfalse
. As a result, the expressionnil && false
will correctly return nil rather than false. This is a source of surprise for some people, but is expected behavior for experienced Rubyists.Solutions
In addition to short-circuit expressions or post-expression conditionals such as:
you should consider the Rails Object#try method, or the Ruby 2.3.0 safe navigation operator.
Use Object#try or Safe Navigation Operator
Object#try
If you're using Rails, the Object#try method is a common way to avoid invoking methods on nil, or other objects that don't #respond_to? the chained method. For example:
This is similar, but not exactly equivalent, to:
Ruby 2.3.0 introduces the safe navigation operator (
&
) which brings similar functionality to Ruby's core.The
&
Safe Navigation OperatorRuby 2.3.0 introduced a new safe navigation operator. This appears to be an actual operator, not a method. This operator lets you chain methods as long as the expression on the left is truthy. For example:
Because it chains methods, and because comparison operators like
>
are really methods rather than parser tokens, you can now do things like:Whether or not you find
object.attr&. > something
more elegant, readable, or useful than other constructs is a matter of opinion, but it's certainly an option in many cases. YMMV.