Err the Blog Atom Feed Icon
Err the Blog
Rubyisms and Railities
  • “case when else end”
    – Chris on August 05, 2006

    I’ve seen this question pop up more than a few times in #ruby-lang. For sooth, I hit this wall once myself: why is my case statement broken?

    Here’s two seemingly equivalent pieces of code:

    the_what = :sealab
    
    if the_what.class == Symbol
        puts "The what is a symbol."
    elsif the_what.class == String
        puts "The what is a string."
    else
      puts "I don't know what the what is."
    end
    

    and

    the_what = :sealab
    
    case the_what.class
    when Symbol
      puts "The what is a symbol."
    when String
      puts "The what is a string."
    else
      puts "I don't know what the what is."
    end
    

    Running the if statement gives The what is a symbol. Running the case statement gives I don’t know what the what is. So, is case broken?

    No. We just made an faulty assumption. Listen: case != if.

    We all know if conditions work by checking for the truthiness of a statement. Is it true or is it false? Simple.

    case works differently. Rather than checking for true or false using ==, case uses === to test expressions. How is this different?

    Unlike, say, PHP, Ruby’s === can be overridden by classes to provide “meaningful semantics in case statements.” So says the Ruby core documentation. In fact, the docs call === “Case Equality.”

    Back to our examples and trusty irb:

    >> the_what = :sealab
    => :sealab
    >> the_what.class == Symbol
    => true
    >> the_what.class === Symbol
    => false
    

    We’re on the right track. Checking the === docs for Module, we see === returns true if the passed object is an instance of the receiver. Since the_what.class gives us Symbol and a Symbol is not an instance of Symbol, we get false. In other words:

    >> the_what = :sealab
    => :sealab
    >> Symbol === Symbol
    => false
    >> Symbol === the_what
    => true
    

    Since the_what is a Symbol, we get true. This explains why our previous case statement is falling through to the final else. Let’s rewrite it with our newfound Ruby powers:

    the_what = :sealab
    
    case the_what
    when Symbol
      puts "The what is a symbol."
    when String
      puts "The what is a string."
    else
      puts "I don't know what the what is."
    end
    

    Running this new code gives us The what is a symbol. That feels much better.

    For further case magic, check out _why’s Wonder of the When-Be-Splat.

  • Zero people have commented.
    Chime in.
    Sorry, no more comments :(
This is Err, the weblog of PJ Hyett and Chris Wanstrath.
All original content copyright ©2006-2008 the aforementioned.