August 28th, 2006

I was recently reminded of this piece of syntax:


  ids = users.collect(&:user_id)

This is equivalent to:


  ids = users.collect { |u| u.user_id }

The implementation is provided by a Rails extension to Ruby’s Symbol class and hence is not available in pure Ruby environments, like irb.

While clearly this is a handy shortcut, it makes your code quite unreadable for anyone who doesn’t understand the to_proc extension made by the Rails team—it is not inherently obvious what you’re doing. If your code is being maintained by multiple developers, is it really wise to use it?

For those of you wondering how this little trick works: the Rails team is exploiting the nature of duck typing in Ruby. When the & operator is used as a function parameter, you are telling Ruby to treat that parameter as the block passed to the function. In order to assure that the object is a Proc object, Ruby calls to_proc on all such parameters. Hence, the Symbol class has been extended by the Rails team to include a to_proc method. This method returns a Proc which calls the method by the same name as the symbol itself.

6 Responses to “Symbol#to_proc: hack or syntactical sugar?”

  1. Ryan Norbauer Says:
    Hi JD, I'm so glad to see you post this. I was wondering if I was the only one who thought this was a bit of an ugly hack. Something makes me definitely uneasy about it, in ways that other Ruby syntactical sugar doesn't. I could see it being useful, but it just doesn't seem "conceptually appropriate." Given how much attention has been paid to readability in Ruby and Rails, I wonder what the point of making this one aberrant hack was.
  2. Bob Silva Says:
    Hi jd, I kinda agree with you that it shouldn't be in Rails. It was cool hackery when first introduced but at what cost to understanding the code? Bob
  3. Ryan Norbauer Says:
    Why couldn't they implement something like the following instead? StupidUsers.each.destroy It would be harder to do, but it would make much more sense, semantically than: StupidUsers.each(&:destroy) There's probably some technical hurdle I'm not seeing here, but couldn't one just rewrite each to proxy whatever method you're sending it over to every object in the collection? It definitely would follow Matz's putative "principle of least surprise":http://www.artima.com/intv/rubyP.html.
  4. bang bros Says:
    Newbie here, but now newbie to the article!
  5. test Says:
    test
  6. sex Says:
    sex

Leave a Reply