Do, or do not. There is no #try.

One of the ways you know you are working in a good language is that it makes bad habits ugly. To wit:

This is not pretty. It is, as my dad might say, “sick and ugly and it wants to die”.
(Yes, your dad would say that. KAG)

It is also, not coincidentally, completely unnecessary. Ruby is a language in which ugly things are more often than not superfluous.

Before I get to rewriting it, let me talk a little about the underlying problem here. The problem is uncertainty. And #try is not the answer. In fact, I’ll come right out and say it: #try is a code smell. It’s a good thing it’s just in ActiveSupport and not in Ruby proper. #try is a thin layer of faded 70s wallpaper pasted over a glaring violation of the “Tell, Don’t Ask” principle.

There is almost never a good excuse to use #try. The example above would be better written with fetch:

In some cases, where you have control over the original Hash, it can make more sense to give the Hash a default value other than nil:

In other cases, when you have a really deeply nested structure, a Null Object might be more appropriate:

But maybe a Null Object is a bigger gun than you want to haul out for this. I got
to thinking about this problem today, and here’s what I came up with, with some help from Larry Marburger:

Here’s the implementation of DeepFetch (also available as a Gist):

Notable about this implementation:

  • There is no mucking about with #respond_to? or rescue.
  • It’s not just for hashes: this will work with any container that supports #fetch and #[].

I hope someone finds this useful.

EDIT: The discussion of #try and its design ramifications is continued in the next article.