jlaine.net

&lified

UPDATE Now ampersand even has its own blog.

Ampersand has received a lot of press lately and I decided to put my oar in as well.

My recent history with ampersand is kind of embarrassing, to say the least. I was giving a Ruby class in UK a few years back and during a class, I asked a participant whether there is some other term for the at sign in English. “Sure”, she said, “it’s ampersand.” To this day I’m not sure whether she thought I said et sign, or genuinely mixed up asperand and ampersand. But for quite some time after that, I happily treated @ as ampersand.

But neither linguistics or typography is the topic of today’s article. Let’s rather talk about ampersand (the real one) in Ruby.

In Ruby, an ampersand denotes a block parameter. But let’s not get ahead of ourselves. A little background might be in place.

One of the coolest features in Ruby are code blocks, or closures. They are basically anonymous functions, but as pretty much everything in Ruby, they are also objects. They are anonymous functions, that have little use just by themselves. However, you can turn them into Proc objects either with the Proc.new constructor or using the lambda kernel method. The common best practice to mark code blocks is to use the curly braces with one-liners and do..end with blocks that span multiple lines:

yell = lambda { puts "AAARGH!!!" }
whisper = Proc.new do
 puts "whee"
end
yell.call
whisper.call

➥

AAARGH!!!
whee

Maybe the most useful application of Proc objects in Ruby are block parameters. Anyone with some knowledge of Ruby is familiar with the following:

>> arr = %w(apple orange kiwi)
=> ["apple", "orange", "kiwi"]
>> arr.each {|i| puts i[0,2]}
ap
or
ki
=> ["apple", "orange", "kiwi"]

As you can see, in this case you need to use neither Proc.new nor lambda with the block; it’s converted to a Proc object implicitly. But as the method receives just a Proc object as its parameter, you could also say something like this, right:

>> put_two = lambda {|i| puts i[0,2]}
=> #<Proc:[email protected](irb):41>
>> arr.each(put_two)

Err, not quite. It turns out Ruby methods can take two kinds of parameters—a number of normal parameters and a block parameter. In the code above the interpreter will think that the put_two Proc object is passed as a normal parameter to the each call, and since each doesn’t take any normal parameters, you will get an error:

>> arr.each(put_two)
ArgumentError: wrong number of arguments (1 for 0)
	from (irb):42:in `each'
	from (irb):42

And this is what brings us back to the ampersand:

>> arr.each(&put_two)
ap
or
ki
=> ["apple", "orange", "kiwi"]

So the ampersand is used to tell the interpreter that the following reference is the block parameter of the method.

One fairly common idiom in the Rails world is this kind of construct:

>> arr.map(&:length)

It is effectively the same as

>> arr.map {|i| i.length }
=> [5, 6, 4]

However, if you start a plain irb session, you will notice something isn’t quite right:

>> arr.map(&:length)
TypeError: wrong argument type Symbol (expected Proc)
	from (irb):2

That’s right. The cool shorthand method that worked so nicely in your Rails app doesn’t work in plain Ruby. That’s because there is some Rails magic behind the &:method call. This magic is e.g. the reason why Ezra has prohibited using the shortcut in Merb framework code.

But let’s have a closer look at what’s actually happening behind the scenes in the shortcut. The thing that’s different from our earlier calls is that there is a colon between the ampersand and the word “length”. This means that we’re not using a variable or method called length, but the symbol :length. If you’re not familiar with Ruby symbols (or even if you are), reading Josh’s recent article on symbols is a worthwhile read.

Now that we know that we’re trying to pass a symbol as the block parameter to a method (and that it’s not really working, as the error above indicates), we need a way to convert it to a Proc object like expected by the method. Ruby has a slew of type conversion methods that are called implicitly whenever it’s clear that a certain type of object is needed. Inside a string for example, to_s is called automatically for every object that is not a string itself:

>> "Nice array: " + arr
TypeError: can't convert Array into String
	from (irb):6:in `+'
	from (irb):6
>> "Nice array: #{arr}"
=> "Nice array: appleorangekiwi"
>> "Nice array: " + arr.to_s
=> "Nice array: appleorangekiwi"

In the same vein, since a block parameter of a method needs to be a Proc object, to_proc is called automatically for all other objects in an effort to get a hold of a proc. So could it work if we just added a to_proc method to the Symbol class? Let’s find out!

class Symbol
  def to_proc
    lambda {|i| i.send(self)}
  end
end

Here we make to_proc a lambda function that will use the send method to call the method with the same name as the Symbol object in question (self) for the element that’s passed to it. (That got too confusing so let’s just use examples). So

arr.send(:length)

is the same as

arr.length

And thus

:length.to_proc

would become

lambda {|i| i.send(:length)}

Now, let’s see how our new method performs:

>> arr.map(&:reverse)
=> ["elppa", "egnaro", "iwik"]

Perfect!

While Symbol#to_proc is a clever and perhaps an elegant hack, it’s still kind of a hack. So should you use it in your code? I tend to side with Ezra on this. If you’re writing framework code, you should probably err on the side of readability and common usage, and thus avoid “magical shortcuts” like these. But in application code, why not. I certainly do.