Ruby Pretzel Colons

The Ruby Pretzel Colon is one of my favorite idioms. But it's salty goodness can be an acquired taste for new Ruby programmers. There seems to be an aversion to it's apparent magic. It is an unusual syntax. But it is easy to understand if you break it down into it's parts.

Here is an example of the pretzel colon:

# general style:
[person1, person2, person3].map { |person| person.name }


# pretzel style:
[person1, person2, person3].map(&:name)

The pretzel colon style &: is more concise and focused and makes the perfect snack at the ball game.

Let's break down what is happening here. In the general style, the map is iterating through the array and passing each person object to the block. The block is defined to take a single argument of a person, the block then calls person.name and returns the name of the person.

Using pretzel style, the result is the same, but the block is built for us using the & operator. The & operator calls #to_proc on the object then converts the result into a block. If our object was a lambda or Proc, they return self in their default #to_proc method. But in this case, our object is a Symbol.

The default implementation of #to_proc on Symbol is to return a Proc that takes the first argument and sends itself to the argument. Example pseudo code:

class Symbol

  def to_proc
    Proc.new { |object| object.send self }
  end

end

The third act of a magic trick is called the Prestige. Using the default #to_proc defined in Symbol, combining it with & which calls #to_proc and converts the result into a block. We are building a block that takes the person object and sends the symbol to it. Sending :name to person is the same as calling person.name.

# expanded pretzel
[person1, person2, person3].map { |person| person.send :name }

The combination of the Ruby & operator's ability to convert objects into blocks and the default implementation of Symbol's #to_proc combine into a magic pretzel. This makes our code more concise and expressive.

A bit of history on the pretzel colon. This idiom started in Rails ActiveSupport but was eventually moved to Ruby Core. It was removed after Rails 2.3. The git history of the file shows "Marcel Molina authored on Nov 20, 2005". The pretzel colon is almost 10 years old.

see https://github.com/rails/rails/blob/2-3-stable/activesupport/lib/active_support/core_ext/symbol.rb

by Chris Mar