h

Being Agile

Pete Hodgson's blurgh

Fun With Symbol#to_proc

As I start to get fluent in ruby I often find myself using a more functional programming style, particularly when dealing with collections. That generally leads to lots of little block-centric operations. I recently discovered a really elegant trick to make that code a little nicer.

Let’s say there’s a Person class:
class Person
    attr_accessor :first_name, :last_name, :age
end
and I have a collection of people which I’d like to sort by age. An obvious approach would be:
people.sort_by{ |person| person.age }
That works, but it is a little wordy. This is ruby, after all! Wouldn’t it be nice if we could write this instead:
people.sort_by &:age
Isn’t that neat? The magic that makes this work is this little monkey patch to the Symbol class.
class Symbol
  def to_proc
    Proc.new { |*args| args.shift.__send__(self, *args) }
  end
end
Note that this is already done for you in Rails and in Facets - in fact that’s where I lifted this code from.

So how does this work? When we called sort_by we passed in the :age symbol, but we used the & to indicate that it should be treated as a block. When you supply a method with a block which isn’t a Proc instance ruby helpfully attempts to coerce it into a Proc by calling to_proc. Because of the Symbol monkey patch to_proc now returns a block which will invoke a method with the same name as the symbol itself on whatever object it is passed in. Phew. Re-read that last sentence 5 times. The end result is that the following two blocks are identical:
block_a = :some_method.to_proc
block_b = proc { |x| x.some_method }
It follows that the following two operations are identical:
people.sort_by{ |person| person.age }
people.sort_by &:age
Voila! I should point out here that I’m not exactly the first person to discuss this great trick. See for messirs Thomas and Williams.

In fact, it’s such a common idiom that it will apparently be baked right into ruby 1.9.