fun with Symbol#to_proc
April 1, 2009
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:
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:
In fact, it's such a common idiom that it will apparently be baked right into ruby 1.9.
Let's say there's a Person class:
class Person attr_accessor :first_name, :last_name, :age endand 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 &:ageIsn'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 endNote 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 &:ageVoila! 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.