Skip to navigation

bangbang your nil is dead

Published March 26, 2007 - Updated August 05, 2007

In Ruby, methods ending in question marks should return booleans.

Update I: the above statement is misleading, if not incorrect. You shouldn’t ever need to check for equality to true or false (if logged_in? rather than if logged_in? == true), and its arguably more the Rubyish to let it “just work” rather than force it into explicitness.

For example:

@current_user = nil

def logged_in?
  @current_user
end

logged_in? == true  # => false
logged_in? == false # =; false

# hrmm, that's interesting... neither true nor false

@current_user = "A pretend user object" 

logged_in? == true  # => false
logged_in? == false # => false

# hrmm, still neither true nor false. Who am I? Where am I? I'm confused!

One way to solve this would be to redefine logged_in? to look like this:


def logged_in?
  @current_user != nil # or !@current_user.nil?
end

That’s alright, but there’s much more succinct way to do the same thing: use the not operator (!) twice, or in other words; bangbang!


@current_user = nil

def logged_in?
  !!@current_user
end

logged_in? == true  # => false
logged_in? == false # => true

@current_user = "A pretend user object" 

logged_in? == true  # => true
logged_in? == false # => false

# much better! :)

...and, if you’re as sick and twisted as my colleague David Lee, you could abuse this to make a blasphemic unimplemented test method:


def test_my_code_should_do_something
  !!?!
end

The moral of the story: you usually want to chuck a double bang (!!) in a boolean method. The most common exception to this humble moral is that if you’re just calling another boolean method (such as include?) from within yours you can assume it does the right thing and leave out the bangbang!

Update II: ignore this unmoral blasphemy. Use a double bang at your own discretion when you want to force some object into an explicit true/false. I’ve sometimes found it useful when I want methods ending with a question mark to return true/false.

Update III: I really feel I jumped the gun with this article and could have encouraged some cargo culting for recommending you use it in boolean methods and not explaining the double-negation itself. I also should have stated my opinion at the time more clearly: “though boolean methods work quite naturally by returning an object, nil, true or false I prefer them to always return a true or false”. I see how forcing it into a boolean is un-Rubyish though I still feel there’s an expectation that boolean methods return booleans, not to mention its a convention which Ruby core itself upholds

Archived comments

Comments were previously allowed on articles. Though no new comments are being accepted you can see the old comments below.

  1. Lachie

    I like your bangbang trick. But! I think that the counterpoint to your general argument is that we should trust the axioms of the language. Its where the power comes from.

    In Java, nothing is a boolean but a boolean. I kind’ve feel that’s the logical conclusion of the argument here.

    The most basic kind of Ruby’s duck typing is that only false and nil are considered to be false.

    I reckon that explicitly testing logged_in? false or logged_in? true obviates the advantages of duck typing.

  2. Tim Lucas

    You’re probably right Lachie, I just thought as Ruby seems to be explicit, and it was rarely the case where a predicate method returned anythin but a true or false value it was best to standardize. To return anything else seemed illogical.

    I’ve scoured ruby-talk and found two threads: booleans and Zero is true. I guess Dave Thomas hits the nail on the head: why have true/false at all? I figured if they existed it made sense to use them as the result of predicate methods, but it looks that it wasn’t always the case.

  3. Lachie

    That’s a good point about having true and false at all. I guess it allows for nuances.

    My perspective is a bit historical: in perl 0, undef and ”” are all false. The Ruby way seems so much nicer than perl, that I find it hard to fault :)

    Just remembered this: http://rhg.rubyforge.org/chapter02.html

    Underneath, false, true and nil aren’t objects at all. They’re just #defined integers.

    Qfalse is actually 0, so that it can be considered false in C as well. (Qtrue is 2 and Qnil is 4)

  4. Evgeny

    This is wrong. Please see my comment on http://www.johnwulff.com/articles/2007/04/11/double-not

    in short: x = false !x.nil? == !!x => false
  5. Tim Lucas

    @Evgeny: The only thing that’s wrong is claiming !!x == !x.nil?, which was John’s typo, not mine :)

    irb> !!true
    => true
    irb> !!false
    => false
    irb> !!nil
    => false

    ...which is exactly what you’d expect, I’d expect.

  6. Evgeny

    But what if my variable/method is a boolean? And it has 3 states, true/false/nil … then the !! will transform the nil into a false, which is not what .nil? does.

    For example – if I want to save a gender of a person in the database as a boolean. I could have a method like so:

    
    def gender_to_text(gender)
      raise if gender.nil?
      return "female" if gender
      return "male" 
    end

    But if I use:

    
    def gender_to_text(gender)
      raise if !!gender
      return "female" if gender
      return "male" end
    

    That would not be the same method, so in this case !! is not .nil?. Or am I missing something?

  7. Tim Lucas

    Evgeny: Sorry if my article is confusing… I’m not claiming !! == .nil?. Double negation is most useful when you know something is going to be some object or nil (like in my example from production code) or where you want both a NilClass or FalseClass to return false, and anything else to return true (just like how the attribute boolean methods work in ActiveRecord).

    re. your gender_to_text method: why are you storing a boolean in the first place? To save row space? Regardless of your reason, how you store that attribute in the database shouldn’t matter to anything but the Person class (and especially not to a helper method such as gender_to_text)

    class Person < ActiveRecord::Base
      def gender
        read_attribute(:gender) && :female || :male
      end
      def gender=(gender)
        write_attribute(:gender, gender.to_s == :female)
      end
    end
    

    Then you can go @person.gender.to_s, which is a more Ruby way of saying gender_to_text(@person.gender).

Articles

toolmantim

I'm Tim Lucas, a user experience designer and developer currently in Sydney Australia.

I run a web application design and development company Agency Rainford, present on various technical topics, snap the occasional photo, tweet my going-ons, share teh codes and post other tid-bits to the tumble.

Most recently I published LAN hacking with Bananajour (June 11, 2009)

Shoot an email to and say hello.

Powered by social networks