Ruby is renowned for both its readability and its flexibility. The ultimate goal of every Ruby program is to work well while appearing as simple as possible.
To achieve this as a Ruby programmer, you’re encouraged to push a lot of the fiddly bits of code deep into parts of your program where a casual reader of the source can ignore them.
Out of the box Ruby ships with some very powerful tools to help you do this, the most famous of which is its syntax for custom blocks. This allows you to re-use existing fiddly code (for example iterating over a list) but to insert a little bit of customisation (so now you can iterate over a list doubling as you go).
The & operator
Blocks are only the beginning of this ability as Ruby has the &
operator which
can be used to “cast” any object to a block. This behaviour works out-of-the-box
on Procs, Methods, and Symbols.
You will have seen the Proc support in action a lot, because that’s
how you pass &block
from one function to the next. Method objects are rarely
used in Ruby, though sometimes inserting .tap(&method(:puts))
is a useful way
to debug an object in the middle of an expression.
The most interesting example of &
however is on the Symbol class. This was
ported to the core Ruby language in version 1.8.7 because it was so popular
with users of the libraries that implemented it (the Ruby Extensions
Project and
ActiveSupport).
The whole purpose of using &
with a symbol is to generate more readable code,
compare:
This second point is the one that has the biggest impact on people reading your code. The mental machinery that programmers use to track variables is necessarily hefty, so if they don’t need to invoke it so often they will find that your code takes less effort to understand.
&X itself
The ampex library takes the idea from
&:symbol
, and adds a little more flexibility. This means that you can get all
the punctuation-free, variable-free goodness, but more often!
This last example gives you something similar to the new
Enumerable::Lazy
module
added in Ruby 2.0; but without having to wait for that to be released. If you’re
interested in more that ampex can help you with, the
README contains a few more examples.
Installation
The ampex library is distributed as a rubygem, so to use it, you can either install it one-off:
$ gem install ampex
Or add it to your Gemfile
.
source :rubygems
gem 'ampex'
We’ve been using ampex in production for over a year now, and beacuse it’s written in pure Ruby, it works on Ruby 1.8.7, Ruby 1.9 and JRuby out of the box.
Further Thinking
While ampex helps a lot, there are still cases where you need to introduce a
temporary variable unnecessarily. As mentioned above, for some of these cases
you can use &method(:foo)
; but I find that that code makes me think even
harder than the equivalent block version.
I would really love to see something like Scala’s underscore in Ruby itself. There have been a few attempts at this, mostly catalogued in Reg Braithwaite’s comprehensive guide to Anaphora in Ruby, for example RubyUnderscore adds it to Ruby 1.8.7 by syntax rewriting and this patch adds it to Rubinius, but none are yet ready to actually be used.
Particularly I think it should be possible with some ingenuity to come up with a solution that covers another 90% of the things you want to do. For example, with pure Ruby and a little cunning, the following could be made to work:
[1, 2, 3].map(&».puts(X))
The question, I suppose, is whether or not it can be done simply enough…