A better try — chaining methods and nil

Of #try, #andand, and #do_or_do_not, the one I like best (and I don’t even like any of them that much, but that’s my next post) is #try.

What they are all basically for is chaining method calls when one of methods might return nil, resulting in a NoMethodError. Here’s Chris Wanstrath’s implementation:

class Object
  ##
  #   @person ? @person.name : nil
  # vs
  #   @person.try(:name)
  def try(method)
    send method if respond_to? method
  end
end

It’s almost right!

First of all, I’d argue that if #try is trying to be better than something, it had better be better than @person.name if @person. That looks and reads so nicely I can scarcely see why it needs replacing. @person.name if @person is so obviously better than @person ? @person.name : nil that I hope people are using that before they start thinking about monkey patching Object (which I hear is destroying Ruby). Anyway, I’d fix the documentation first.

But I think the biggest problem with #try is that it’s not doing what it says it’s doing. It’s not a replacement for @person ? @person.name : nil. That’s certainly not what the method is doing. Here’s what I’d do:

class Object
  ##
  #   @person.name if @person
  # vs
  #   @person.try(:name)
  def try(method)
    self.send(method) if self
  end
end

See? Much clearer; it does what it says. Using respond_to? means that the original #try is about something else; it’s about whether or not an object responds to a message. This #try is about sending a message to an object unless it’s nil or false, which is closer to what the original use case was… right?

But then it occurred to me that the issue is nil, not nil or false. The problem isn’t getting false in the middle of method chains, it’s nil. So maybe what #try should really be is this:

class Object
  ##
  #   @person.name unless @person.nil?
  # vs
  #   @person.try(:name)
  def try(method)
    self.send(method) unless self.nil?
  end
end

I might even use that.

RSS feed for comments on this post · TrackBack URL

Leave a Comment