10

I'm new to Ruby and I was surprised when I found out that all objects are true apart from nil and false. Even 0 is true.

A nice thing about that property of the language is that you can write:

if !variable
  # do stuff when variable is nil
end

My colleagues, who are more seasoned Ruby developers, insist I should choose that instead of using .nil? like so:

if variable.nil?
  # do stuff when variable is nil
end

However I believe that the latter is a better option for two reasons: 1. I think it's more object oriented, especially in a language like Ruby where everything is an object and message exchange. 2. It is more readable, in my subject opinion, even if it is less compact.

Am I making a "newbie" mistake here?

  • 4
    Are you testing for nil? or a false value? – user40980 Nov 13 '15 at 19:20
  • I'm testing for nil. Basically if the variable is nil, I do something (or not) – Javier Holguera Nov 13 '15 at 19:26
  • 3
    If you are testing for nil, why do you want the test for false to also enter that block? – user40980 Nov 13 '15 at 19:26
  • Agree, I pointed that out as another reason to prefer .nil? But they insisted that "if variable do x" is more idiomatic than "if variable.nil? do x" and preferable when you know that variable is not boolean – Javier Holguera Nov 13 '15 at 19:28
16

Write what you mean. Mean what you write.

Lets take a different test. .vowel?

if char == 'a'
   # do stuff
end

vs

if char.vowel?
   # do stuff
end

Now, its obvious that these do not do the same thing. But if you're only expecting char to be a or b it will work. But it will confuse the next developer - the second block will be entered into for the conditions where char is [eiou] also. But for a, it will work correctly.

Yes, it is the case that nil and false are the only falsy values in Ruby. However, if you are testing for nil by testing for nil || false, this isn't what you mean.

It means that the next developer (who happens to be a crazy axe murder who knows your home address) will read the code, and wonder why false should go in there too.

Write the code that conveys exactly what you mean.

3

Consider the following:

response = responses.to_a.first

This will return the first element of responses, or nil. Then because conditional statements treat nil as false we can write:

response = responses.to_a.first
if response
  # do something ('happy path')
else
  # do something else
end

Which is more readable than:

if response.nil?
  # do something else
else
  # do something ('happy path')
end

The if (object) pattern is very common in Ruby code. It also leads nicely to refactoring, e.g.:

if response = responses.to_a.first
  do_something_with(response)
else
  # response is equal to nil
end

Remember also that a Ruby method returns nil by default. So:

def initial_response
  if @condition
    @responses.to_a.first
  else
    nil
  end
end

can be simplified to:

def initial_response
  if @condition
    @responses.to_a.first
  end
end

So we can further refactor:

if response = initial_response
  do_something_with(response)
else
  # response is equal to nil
end

Now you can argue that returning nil is not always the best idea, since it means checking for nil everywhere. But that is another kettle of fish. Given that testing for object "presence or absence" is such a frequent requirement, the truthiness of objects is a useful aspect of Ruby, albeit surprising for newcomers to the language.

2

Not only is if variable.nil? read more like natural language, it also guards against a programmer accidentally assigning it a false value and falling into your if statement, since Ruby is a loosely typed language.

  • 1
    So the initial comment in the original question was wrong? Should have been "do stuff when variable is nil or false"? And since code not matching comment is a bug, there's a bug straight away? – gnasher729 Nov 15 '15 at 17:46
  • Correct. A simple truthiness test could introduce logic errors. – Greg Burghardt Nov 15 '15 at 18:09
0

Idiomatically, unless variable is preferable to if !variable. Negated if statements are often considered a code smell as it's easier to miss the negation when skimming.

The bigger picture here is that doing nil checks like this is also a bit of a code smell. For proper OO, aim to use the null object pattern so that this sort of check is never needed. http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/

Tell objects what to do, don't ask them what they are. This is sometimes known as tell don't ask

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.