RUBY: DRY up your Enumerations
May 25th, 2007 byUsing Higher Order Messaging to honestly reveal your intentions.
If you had a collection of stooges (@stooges) and you were looking for Mo, would you rather say:
@stooges.select {|s| s.name == ‘Mo’}
or
@stooges.that.have.name == ‘Mo’
I don’t have to ask you twice, right?
In the first example, the fact that it’s an enumeration overwhelms the intention to find Mo.
In the second example, the most obvious thing is that I want Mo. The fact that @stooges is a collection is obviously not the most important thing. What matters is that I want Mo and example 2 does a much better job of revealing that.
This is not magic. It’s Higher Order Messaging. And trust me, you want it.
I stumbled across HOM on Why’s blog over a year ago. I followed a link from his site to some other site and downloaded ho_enumerable.rb (sorry, I can no longer find the orginal site, but if you know, let me know and I’ll make proper attribution here). I’ve been using HOM with delight ever since.
What is Higher Order Messaging?
Higher order messaging lets you talk to collections in a natural way that reveals instead of conceals your ultimate intentions. Here are more examples.
(To follow along at home, download hom_examples.rb and ho_enumerable.rb).
Given class Stooge:
class Stooge
attr_reader :name, :hair, :habit
def initialize(aRow)
@name = aRow[0]
@hair = aRow[1]
@habit = aRow[2]
end
def baldish?
hair.nil?
end
def muck_with_name
name.gsub!(/y/, ‘x’)
end
def to_s
“#<Stooge - name:#{name} hair:#{hair} habit:#{habit}>”
end
end
and array STUFF
STUFF = [[ 'Mo', 'bowly', 'eye poking' ],
[ 'Larry', 'curly', 'whomping' ],
[ 'Curly', nil, 'whining' ]]
I can create a new collection of Stooges by saying
@stooges = STUFF.as(Stooge)
instead of
@stooges = STUFF.collect {|r| Stooge.new(r)}
I can select all of the stooges that are nearly bald by saying
@stooges.that.are.baldish?
instead of
@stooges.select {|s| s.baldish?}
I can find all of the stooges named ‘Mo’ by
@stooges.that.have.name == ‘Mo’
instead of
@stooges.select {|s| s.name == 'Mo'}
I can build an array of all of their names by saying
@stooges.extract.name
instead of
@stooges.collect {|s| s.name}
I can find out if ‘all’, or ‘any’, stooges ‘are’ or ‘have’ some quality. (Since you’re completely sold now I’m gonna leave the iteration style code to you).
@stooges.all.are.baldish? # false @stooges.any.are.baldish? # true
@stooges.all.are_not.baldish? # false @stooges.any.are_not.baldish? # true
@stooges.all.have.name == ‘Mo’ # false @stooges.any.have.name == ‘Mo’ # true
I can also order the stooges by some attribute.
@stooges.in_order_of.name @stooges.in_reverse_order_of.name
And I can perform some operation against every stooge.
@stooges.do.muck_with_name
The ho_enumerable code is very short and delightfully nerdly. You may or may not be interested in looking at it to figure out how it works, but you don’t have to understand the details of implementing HOM to recognize what a great idea this is.
Stick ho_enumerable in your rails/lib directory, require the file and start using HOM. Your code will please you by revealing what it’s doing without drowning you in detail about how it’s doing it. And when your friends have to maintain it, they’ll like you even better than you like yourself.
ps - Thanks to Lori Evans for pouring over the ho_enumerable code with me on the plane on the way home from railsconf. It was more than can possibly be expected of any exhausted human being and she was alarmingly cheerful about it. Thanks.

