I’ve been crawling over this Rails project and lately finding lots of places where clarity could be increased by using symbol_to_proc, so I thought I would share it a bit for those not in the know on this handy ruby shortcut.
Rails added symbol_to_proc shorthand in version 1.1, and it is such a nice shortcut, that it became part of the Ruby language in version 1.8.7
So, anywhere you would normally use an iterator with a block like so:
people.collect { |p| p.name }
could be shortened to call like:
people.collect(&:name)
The difference may not look so dramatic in the above example, but how about this one:
# classic prison.inmates.select{ |i| i.repeat_offender? }.select{ |i| i.offsenses }.collect{ |o| o.prosecution_fees }.sum # to_proc prison.inmates.select(&:repeat_offender?).select(&:offenses).collect(&:prosecution_fees).sum
Some people, including Rails core member pratik (http://m.onkey.org/2007/6/30/let-s-start-with-wtf)
Have criticized and advised against using this shortcut because it is roughly 4 times slower than doing without its syntax sugar.
Now, I would rather have a beautiful, understandable code base than a particularly fast one (after all, I *am* using Ruby), but the reason for the slowness is largely because of the implementation as shown below:
class Symbol def to_proc Proc.new { |*args| args.shift.__send__(self, *args) end end
This implementation looks for arguments passed in and shifts self off of them before calling.
This allows for calling a method with arguments like so:
[1,2,3].inject(&:+)
I believe this represents a real edge case at the expense of a lot of speed, and Luke Redpath (http://lukeredpath.co.uk/blog/optimising-symbol-to_proc.html) has a lot of good to say about the topic, and even goes as far to present a patch for how this should be implemented for pure speed:
class Symbol def to_proc @to_proc_proc ||= Proc.new { |obj| obj.__send__(self) } end end
I’ve patched this in to my Rails project and not a single one of my tests failed, your mileage may vary, but even without the speed boost, I’ll continue to use :symbol.to_proc because I love it so.