Ruby Enumerable Magic: Unary Ampersand Operator

  1. The Basics
  2. Unary Ampersand Operator
  3. Booleans
  4. Filters
  5. New Collections
  6. Aggregates

You’ve probably seen this example in my previous post, or in other Ruby code:

irb(main):001:0> ['a', 'b', 'c'].map(&:capitalize)
=> ["A", "B", "C"]

This is a cool trick, which equates to:

['a', 'b', 'c'].map{|letter| letter.capitalize}

Most Ruby developers have seen this idiom, and even used it, but you may not know how it works. This is *not* strictly a piece of Enumerable magic, even though this is probably where you see it used most often. We can all harness this power, but much like the Force it can be used for good or evil.

There are really two pieces of magic used together to make this possible. The first is the unary ampersand operator. Contrary to what you might think, there is no “&:” operator in Ruby. What you’re seeing in my first example above is actually “&” and “:capitalize”, pushed together. That first character is the unary ampersand. The second piece is a Ruby symbol that is being passed to the operator.

When Ruby sees the unary ampersand on the last argument of a method, it tries to convert it to a proc (an executable block of code), and run it. It does this by calling to_proc on the object. This is where the second piece of magic comes in. Rails 1.1 added a to_proc method to the Symbol class, so that you could pass a symbol to the unary ampersand (ie, &:capitalize) and it would convert it for you. Ruby 1.8.7 rolled this change into Ruby itself, so you can use it outside of Rails.

Digging into “The Why” can be fun, but what I really wanted to know is how I could harness this Ruby voodoo (or, if you will, “rooboo”) for my own selfish gain. That was, by far, the hardest part of putting together this guide. The good news is, once I figured it out, it’s easy to pass on.

Let’s use the example of a Translator class with one public method: speak. This class has a method for each of the languages it knows, and a catch-all method (Ruby’s method_missing) for languages it doesn’t understand:

class Translator
  def speak &language
  def french
    'bon jour'
  def spanish
  def turkey
  def method_missing *args
    'awkward silence'

This is awesome! Let’s play with it in irb:

irb(main):001:0> require 'translator.rb'
=> true
irb(main):002:0> translator =
=> #<Translator:0x10038ff08>
irb(main):003:0> translator.speak(&:spanish)
=> "hola"
irb(main):004:0> translator.speak(&:turkey)
=> "gobble"
irb(main):005:0> translator.speak(&:italian)
=> "awkward silence"

The key to this magic is in the speak method:

  def speak &language

There’s a lot going on here. When Ruby converts :turkey into a proc from a symbol, it makes the assumption that a receiver will be passed into the proc, and the proc will be called *on* that receiver. In this case, we pass self into the proc above. This is like calling self.turkey. Gobble.

Is this a super useful example? Maybe not. But it lets you play with the smallest possible working example, and get comfortable. If you have any questions, please leave a comment below.


Tags: , , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: