In Progress
Unit 1, Lesson 1
In Progress

Fail And Raise

Video transcript & code

It's February the 20th, 2014 as I'm making this episode, and today is a sad day. Earlier I heard the news that Jim Weirich had passed away. Jim was a hero of mine, as I think he was to just about everyone in the Ruby community. Of course I remember him for his projects, like Rake, Builder, and RSpec-Given. And also for his wonderful, mind-expanding conference talks. But more than that, Jim had a childlike exuberance that was infectious to everyone around him. Whether talking about the lambda calculus or flying Ruby-powered robots, to be around Jim was to be reminded of the joy of discovery.

Jim was also amazingly generous with his time and his thoughts. As some of you may know, he was a RubyTapas viewer. Despite the fact that half the stuff I show on here I probably learned from him, he still took time to watch and offer feedback and ideas for new episodes.

Back in 2011, before the first talk I ever gave to a conference audience, I emailed Jim asking for ideas for my presentation on Ruby exceptions. I don't think I'd ever met him or emailed with him before then, but in his usual generous way, he sent back a lengthy email summarizing his thoughts on how best to use exceptions.

Today's episode is about one of the ideas he sent me in that email.

In Ruby, when we want to signal a failure, we raise an exception. Specifically, we use the raise method.

raise "Abandon ship!"

This is not the only way to raise an exception however. We can also use the lesser-known #fail.

fail "Abandon ship!"

There is no difference between these methods. They are simply aliases for each other.

You might reasonably wonder why there are two methods to do the exact same thing. I don't know the history behind this particular feature, but it's a fairly common pattern in Ruby. There are a number of method aliases, such as #inject and #reduce, which exist simply because some Ruby programmers preferred one term while others preferred a different one. While some languages go for small libraries and a single way to accomplish any task, Ruby tends to err on the side of letting programmers express themselves whatever way they feel more comfortable.

[1,2,3].inject(:+)              # => 6
[1,2,3].reduce(:+)              # => 6

At this point, most of the Ruby world has embraced #raise over #fail. Not Jim Weirich, though—in his projects, he used both methods.

But he didn't just pick one at random. Jim theorized that, since there were two exception-signaling methods to choose from, we might as well use the choice to convey a little extra meaning to readers of the code.

When indicating a failure for the first time, he would use fail. This seems like a reasonable choice to me, as a way of indicating that something has, well, failed.

For instance, here's a line in the Rake pathmap code, which indicates an unrecognized argument format. You might remember pathmap from episode 132.

fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"

Signaling a problem like this is the most common case. However, sometimes we find it necessary to rescue a specific exception and then re-raise it. For instance, here's some more code from Rake. This code is responsible for requiring extra Ruby libraries from the command line.

['--require', '-r MODULE',
  "Require MODULE before executing rakefile.",
  lambda { |value|
    begin
      require value
    rescue LoadError => ex
      begin
        rake_require value
      rescue LoadError
        raise ex
      end
    end
  }
],

In the specific case where doing an ordinary require raises a LoadError—and not, say, a parsing error—this code rescues the error and then tries again to require the file using the special rake_require method. If that too raises a LoadError, there are no options left. And so it re-raises the exception.

As you can see, here raise is used rather than fail. Jim Weirich's convention was to use fail to signal an exception condition initially, and only to use raise when re-raising an exception. That way, the choice of raise instead of fail provides an extra cue to the reader that something out of the ordinary is going on.

I'm a big fan of conventions that give extra semantic information to code readers. Lately I've started to use this one in my own code, and now that I've introduced it you may start to see it cropping up in RubyTapas episodes.

And that's it for today. Happy hacking!

Responses