I’m in love with :symbol.to_proc

Posted by unixmonkey on August 06, 2010

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.

Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

Comments