December 01, 2008

Posted by John

Tagged opinions

Older: RubyGems: Yours, Mine and Ours

Newer: Watch John Speak, Drink John Drink

Unless, The Abused Ruby Conditional

Some people hate unless in Ruby. I personally do not, but I try to abide by a few rules. I’ll explain with some examples below. The uses that I label as “ok” below could just be personal preference, so I will not refer to them as “good”, but I would definitely avoid using the ones I label as “bad”.

Statement Modifier…Ok

I think unless actually reads better than if ! when used as a statement modifier. For example, I think this…

raise InvalidFormat unless AllowedFormats.include?(format)

…reads better than this…

raise InvalidFormat if !AllowedFormats.include?(format)

I think the reason I like the former incarnation is because it would read like a sentence to almost anyone, even those unfamiliar with Ruby (like my mom and we all know it is important for moms to be able to read code).

Without Else…Ok

I also don’t mind using unless when you are just checking one condition and not using else like so…

unless foo?
  # bar that thing
end

With an Else…Bad

What I dislike is when unless is used with an else like so…

unless foo?
  # bar that thing
else
  # foo that thing
end

If you have code like this, just swap the code doing the work and use if. Trust me, it reads a lot better.

if foo?
  # foo that thing
else
  # bar that thing
end

The if version just feels easier to process than the version with unless. I think using unless with else is like speaking with double negatives. It slows down the next programmer in the code, as they have to think instead of just flowing through the conditional, if that makes sense.

When testing nil?…Bad

As Ryan Bates pointed out in the comments below, testing nil like this…

unless foo.nil?
  # do some foo
end

…is a bit more readable like this…

if foo
  # do some foo
end

unless foo.nil? and if foo equate to the same thing but is kind of a double negative issue, like I just mentioned about using unless with else. Thanks for the another bad unless tip Ryan!

With Multiple Conditions…Bad

The reason I hate (and it is hate, not just dislike as above) unless with multiple conditions is that I always forget whether the “not” applies to only the first conditional or to all of them. If you are wondering, the “not” does apply to all conditionals, but just talking about it has me feeling confused again, so please do not do this…

unless foo? && baz?
  # bar that thing
end

Instead, I would probably do something like this…

if !foo? || !baz?
  # bar that thing
end

Final Thoughts

unless is ok but do not throw it in your code willy nilly. I said “nil” get it? Hehe. Some might say that if unless can be so easily abused, why not avoid using it all. I guess I disagree with that, as I think it does actually improve code readability in some cases, as I mentioned above. Feel free to argue below and I will ignore you. :)

23 Comments

  1. Agreed. Using “unless” can really make your head spin in certain cases.

    Another case to avoid it is with “unless foo.nil?”. Just do “if foo”, it reads much better.

  2. The only people that don’t like “unless” are either:

    A) Old school programmers, which I can understand.

    B) Blog followers, the people that let the hip blogs choose their mindset.

    I’ve personally thought, from back in my C/C++ days, that “if not” sounded like the most obscure piece of dog shit I’ve ever had to read. Not that its hard to understand, but the fact that I never say “If not” in plain English.

    News Flash people: Unless IS If not… and if you see it, you should think that way if you have a problem.

    Now I can jive with your rules above, but people need to settle down and understand that Unless is just one of many things that makes Ruby so much of a pleasure to use for English speaking programmers.

  3. @cheapRoc I’ve often felt the same way about the unless statement in Ruby – it makes various bits of code read like English making it “a pleasure to use for English speaking programmers”. The irony being that Ruby is, of course, originated from Japan!

    I guess Japanese language inventors speak better English than the computer scientists who came up with all the other languages, right? ;)

  4. I believe the “unless foo? && baz?” should be re-written as “if ! foo? || ! baz?”.

    Either on purpose or accidentally, you’ve highlighted one of the problems with “unless” and multiple conditions. :)

  5. Zach Langley Zach Langley

    Dec 01, 2008

    Just want to point out that unless foo? && baz? should be translated to if !foo? || !baz? (the && gets negated as well). De Morgan’s laws.

  6. Just to avoid confusion: your two last examples do different things:

    “unless foo? && baz?” = “if !(foo? && baz?)” = "if !foo? || !baz?

  7. Zach Langley Zach Langley

    Dec 01, 2008

    I butchered that. Wanted it rendered as:

    Just want to point out that unless foo? ==&&== baz? should be translated to if !foo? || !baz? (the && gets negated as well). De Morgan’s laws.

  8. Zach Langley Zach Langley

    Dec 01, 2008

    Bleh. Oh well.

  9. @Rick – Haha. It was accidentally, but I updated the article.

    Thanks for the corrections everyone! The && has been changed to ||. I was just making sure you were all on top of your game. :)

  10. @Ryan – Good tip! Added it to the article. I figured I would forget a few.

  11. Greetings,
    What about this gem:


    begin

    1. do something
    2. do something else
      rescue # something
    3. do something under the error case
      end unless bar?

I kid, I kid… ;)

Interestingly your usage is pretty much the same as what I’ve come to find reasonable.

Oh, and @ryan, I have a mild dislike of if foo as a way of testing nil. It’s not intention revealing; the reader has to think about whether the variable can be nil, or if it’s intended to be a boolean, or (worst) if it’s intended to be a boolean AND can be nil. Thus, unless foo.nil? makes sense to me as a code writer, writing for future code readers.

— Morgan

  • Greetings,

    lol Ah well; our sensibilities are different on the if foo vs. unless foo.nil? issue. I don’t see it as a double negative, as I see foo.nil? as a positive assertion. (Perhaps assertion isn’t the right word, but you probably take my meaning.)

    Of course I never liked if(foo) { } in C/C++ for !NULL testing either. There are some windmills it’s not worth tilting at, though. :)

    — Morgan

  • @Morgan – I can see your point, but I actually like if foo in most cases.

  • While this may not relate directly, I think it reads better. Let’s say you have this:

    do_this if something && !the_other

    That can be re-written as:

    do_this if something unless the_other

    When you break it down, it makes sense. But at first glance, it’s odd that it actually works.

  • @Ryan – Huh. Didn’t know that was possible. I’ll have to digest that a bit before I can decide if I like it or not. I agree at first glance it is odd but still kind of interesting.

  • Brian Hogan Brian Hogan

    Dec 01, 2008

    Heh, I’m gonna use unless all the time.

    I absolutely hate seeing this:

    if !something
    ..
    else
    ..
    end

    Mainly because I don’t see so well. The use of unless makes it much more clear to me what the intention is.

    Now let’s have a discussion on when and when not to use case…select. :)

  • 
    >> puts 'bacon' unless false.nil?
    bacon
    => nil
    >> puts 'bacon' if false
    => nil
    
  • Hey,

    I also like using unless for one liner. like:

    do_something unless condition

    or

    unless this
    do_this
    end

    I hate complicated unless statements

  • The unless keyword is a trap. It’s almost always easier to understand if not.

    
    puts 'foo' if not bar
    

    is easier to read than…

    
    puts 'foo' unless bar
    

    Seems like some people opt for the unless keyword in preference of if !. I agree completely. Consider using the english operator alternative as above (If precedence isn’t important).

  • Oh my gosh. I can’t believe I’m seeing this kind of conversation in … err wait. We are programmers that tend to pick at the tiniest thing.

    Personally I follow the english readable process.

    do something unless some condition
    reads better than
    do something if not some condition
    . A grammar teacher would slap you silly (with a trout no less) if you used the phrase
    if not
    in a sentence.

    So to make this short, use

    unless
    where it makes sense to use it grammatically. Unless of course it doesn’t make sense grammatically to use it :P.

  • Mark Wilden Mark Wilden

    Dec 02, 2008

    This is trivia, but ‘unless’ is not equivalent to ‘if !’ The following is an error:

    
    unless foo
      puts 'not foo'
    elsif fubar
      puts 'fubar'
    end
    

    (Why can’t we preview our comments?)

  • @Mark – You can’t preview because it is low on the priority chain for RailsTips right now but trust me it is on the list and some rainy day I will add it. Already made a killer preview for Ordered List, so at least I have a starting point.

  • I think
    if foo

    end
    is better than the double negative
    unless foo.nil?

    end
    If you don’t know that anything except nil or false is true, don’t come up with a way to bypass that: learn it!
    do_something unless foo.nil?
    is, IMO, stupid
    do_something if foo
    is much better.

  • Sorry, comments are closed for this article to ease the burden of pruning spam.

    About

    Authored by John Nunemaker (Noo-neh-maker), a programmer who has fallen deeply in love with Ruby. Learn More.

    Projects

    Flipper
    Release your software more often with fewer problems.
    Flip your features.