Higher Order Messaging in Ruby

Sorting Mail

I've recently been experimenting with Higher Order Messaging, a design pattern that can be used for querying and manipulating collections of objects in a purely object-oriented manner.

On the way back from JAOO last week I whipped up an implementation in Ruby and applied it to some of my Ruby code. I was very pleased with the result. Higher Order Messaging transformed laborious code that queried and manipulated objects in collections into succinct statements that clearly expressed the application rules being implemented.

What is a Higher Order Message?

A higher order message is a message that takes another message as an "argument". It defines how that message is forwarded on to one or more objects and how the responses are collated and returned to the sender. They fit well with collections; a single higher order message can perform a query or update of all the objects in a collection.

It's probably easiest to look at an example.

The following code allocates government benefits to claimants. To start, here's the definition of a benefit claimant.

class Claimant
    attr_reader :name
    attr_reader :age
    attr_reader :gender
    attr_reader :benefits

    def initialize(name, age, gender)
        @name = name
        @age = age
	@gender = gender
	@benefits = 0
    end

    def retired?
        @gender == :male && age >= 65 ||
        @gender == :female && age >= 60
    end
    
    def receive_benefit(amount)
        @benefits = @benefits + amount
    end
end

In the following code snippets, the variable claimants contains an array of Claimant objects.

Let's compare different ways of implementing the following business rule: retired claimants receive benefits of 50 a week.

Here's how to implement this rule by explicitly iterating over the list of claimants using Ruby's for statement:

for claimant in claimants
    if claimant.retired? then
        claimant.receive_benefit 50
    end
end

Here's the same rule implemented with higher-order functions — methods that accept a block:

claimants.select {|e| e.retired?}.each {|e| e.receive_benefit 50}

And finally here's the same rule implemented with higher order messages:

claimants.where.retired?.do.receive_benefit 50

I think that the code using higher order messages most succinctly expresses the business rule being executed. It expresses what is being performed and hides the details of how. In comparison, the code with explicit iteration mingles business logic within procedural control flow and the code using blocks has a lot of syntactic noise and additional block parameters.

The higher order messaging version does have messy dots between messages, but unfortunately that's an aspect of Ruby we can't change. At the risk of sounding like a frothing evangelist, I have to admit that the code would be neater in Smalltalk:

claimants where retired do receiveBenefit: 50.

I'd also prefer to use the name "each" instead of "do", but the name "each" has already been grabbed by the Ruby Enumerable mixin.

Anyway...

In the example above there are two higher order messages, where and do. The where message returns an array containing those elements of a collection for which the following message returns true. The do message sends the following message to all elements of a list and returns nil. The clause "claimants.where.retired?" returns a list of claimants that are retired, and that list is then sent "do.receive_benefit 50". Thus, the single line means the same as:

retired_claimants = claimants.where.retired?
retired_claimants.do.receive_benefit 50

Higher order messages provide a convenient framework for querying and manipulating collections of objects that has the additional benefit of clearly expressing domain logic. Thanks to Ruby's open classes, I added the higher order messages to the Enumerable module, automagically defining them for Ruby's built-in arrays and all other enumerable classes.

More Useful Higher Order Messages

I've found the following higher order messages to be useful when working with collections.

Having filters a collection by a predicate applied to the result of a query sent to each element. This actually combines two higher order messages. For example, to give an additional benefit to claimants older than 100:

(claimants.having.age > 100).do.receive_benefit 25

Unless is the opposite of "where"; it returns the list of elements for which a predicate returns false:

working_claimants = claimants.unless.retired?

In_order_of and in_reverse_order_of sort on the value of an attribute:

sorted = claimants.in_reverse_order_of.benefits

Sum calculates the total of an attribute:

total_benefits = claimants.sum.benefits

Extract returns a collection containing the values of an attribute of the elements:

names = claimants.extract.name

Limitations of Higher Order Messages

Of course, there are limitations to what you can do with higher order messages. Most obviously, you can only pass a single message to a higher order message. This means, for example, that unlike the Array#select method, which accepts a block, the higher-order ?where message cannot accept a general logical expression as a predicate to filter the collection. Instead, you must define the predicate as a method of the objects in the collection.

On the other hand, it's impractical to move all the code that manipulates an object into methods of its class. Take, for example, a report of benefits per claimant that is implemented as follows using blocks.

claimants.sort {|c1,c2| c2.benefits <=> c1.benefits}.each do |c|
    puts "#{c.name}\t#{c.benefits}"
end

Implementing that report with higher order messages would require adding a "write_benefit_report_line" method to the Claimant class. This would mix presentation logic with application logic and reduce the cohesion of the Claimant class: not a good design decision.

To avoid this problem, I added a method to Enumerable that returns a collection of adapter objects that wrap the elements of the original collection. To write the benefits report above, I would now write a BenefitsReport class that can report the benefits for one claimant and create a collection of BenefitsReport objects from a collection of Claimants, as follows:

class BenefitsReport
    def initialise(claimant)
        @claimant = claimant
    end
	
    def display
        puts "#{@claimant.name}\t#{@claimant.benefits}\n"
    end
end

claimants.in_reverse_order_of.benefts.as(BenefitsReport).display

But are these really limitations? After using higher order messages for a while I've come to think that they are not. The first limitation encourages you move logic that belongs to an object into that object's implementation instead of in the implementation of methods of other objects. The second limitation encourages you to represent application concepts as objects rather than procedural code. Both limitations have the surprising effect of guiding the code away from a procedural style towards better object-oriented design.

Higher Order Messaging in Other Languages

It's easy to implement Higher Order Messaging in any dynamically typed OO language that can capture unknown messages. It was originally written in Objective C and Smalltalk and I wrote the Ruby implementation in only a couple of hours on the plane. I've described the implementation details in another post. I think this pattern would be most useful in languages that do not have a succinct syntax for higher order functions, such as Python.

I also tried, and failed, to implement higher-order messaging in Java 5 using the java.lang.reflect.Proxy class. If anybody can implement higher-order messaging in Java I'll be very impressed. Please let me know if you do.

Update: I've written a short post about how to implement this in Ruby.

Update: The code is available on RubyForge in the Homer project for anybody who wants to play with it.

Posted on October 6, 2005 [ Permalink | Comments ]