How Elvis showed me a neat way of using operators in Ruby

Recently the Groovy team introduced a new operator to the Groovy language. It is called the Elvis operator. There is one thing I particularly like about this operator. It’s name.

To bad the Elvis operator is only a shortening of Java’s ternary operator, written like ?:. One use-case for the operator is returning a ‘sensible default’ value if an expression resolves to false or null. A simple example might look like this:

[groovy]
groovy:000> name = null
groovy:000> displayName = name ?: “anonymous”
===> anonymous
[/groovy]

I had a good laugh about it with some people at JavaPolis, and forgot about it. Until I noticed something while reading the expressions chapter in the PickAxe (I’m trying to boost my Ruby knowledge).

In Ruby anything which isn’t nil is true in an expression. Due to this behavior the result of a shortcut expression doesn’t have to be a boolean but is actually the value which evaluated to true (in Groovy the result is a boolean). This means Ruby implicitly has a ‘Elvis’ operator.

[ruby]
irb(main):069:0> name = nil
irb(main):070:0> displayName = name || “anonymous”
=> “anonymous”
[/ruby]

Even better, Ruby allows you to use the shortcut OR in combination with the assignment operator:

[ruby]
irb(main):072:0> name = nil
irb(main):073:0> name ||= “anonymous”
=> “anonymous”
irb(main):074:0> name = “peter”
irb(main):075:0> name ||= “anonymous”
=> “peter”
[/ruby]

Funny to see how a slightly different behavior of something like to or operator makes it even more useful!

This entry was posted in groovy, ruby. Bookmark the permalink.

5 Responses to How Elvis showed me a neat way of using operators in Ruby

  1. railsguru says:

    It’s very useful to init a variable which might have been initted before, at least that’s where I use it most frequently e.g.:

    queue ||= []

    // now you’re sure you have a valid queue array

  2. mich.barsinai says:

    I’m afraid that is just another case where ruby tries to be nice and then stabs you in the back unintentionally. There is a very good reason for groovy to go with “?:”, and that is values inited to false.

    display_icons ||= true
    would *always* set display_icons to true, even if it was inited to false.

    Suppose you have a rails application. And suppose that you have a partial which you render with a bunch of locals. One of them might be “display_icons”, which you want to be true unless otherwise defined. You put the above snippet at the top of the code (real gurus would probably iterate on the keys and of the hash to do that, and would get bitten much harder). You then render the partial with :locals=>{:display_icons=false}, and you would still get the icons.

    Operator overloading is trickier than one might think. There’s a reason why it’s not in java (see also here: http://cafe.elharo.com/java/operator-overloading/).

    BTW, if ?: called Elvis (why?), I propose we call the ruby || operator “Wham!” (it does look like 2 skinny guys, and it is the sound your forehead would make when hitting the keyboard after you realized why those icons were showing all the time :-) .

  3. peter says:

    Thanks Mich, this is a very valid point. The Elvis operator ‘suffers’ from the same problem:

    groovy:000> a = false
    ===> false
    groovy:000> c = a ?: true
    ===> true
    

    And yeah, I like the “Wham!” concept!

  4. levi_h says:

    >> display_icons ||= true
    >> would *always* set display_icons to true, even if it was inited to false.

    But that’s what you’d expect, right? That the || (or ‘or’) operator would return true for true and false? And with that in mind, you’d more likely write something like display_icons = (display_icons.nil? ? true : display_icons).

    As I understand it, C# has the same operator, ??, that returns a default value if the lhs is null. It would be nice to have an operator like this in Java as well: it especially pays off with longer expressions, which almost require you to introduce a local variable.

  5. peter says:

    @levi yes, those results are as expected… it’s Rubys’ way of avoiding having booleans with three states.

    Your fix works, but personally I would avoid using the ternary operator in Ruby to solve this… I’d use a trailing if solution:

    display_icons = true if display_icons.nil?
    

    By the way, since autoboxing was added to Java this is sort of a problem:

    Boolean b = null;
    
    if(b){
      System.out.println(b)
    }
    

    This throws a NullPointerException during unboxing; whereas Ruby or Groovy evaluate the expression correctly (assuming that the uninitialized state of a boolean is false).

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>