Implementing Higher Order Messages in Ruby
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. How is this actually implemented?
Under the hood, a method for the higher order message creates and returns a new object that represents the higher order message. This object can capture any message, forward that message on to all the elements of the collection and collate the results that they return into a result that is passed back the sender of the higher-order message.
In Ruby, messages are
captured by defining a method named "method_missing".
"Method_missing" is invoked with the name and parameters
of a message received by an object when the object has no explicit
method for that message. That means, however, that a little more
magic is required to implement higher order messages. Classes
inherit a lot of methods from the Object class. These must be
undefined so that they can be captured by method_missing. This is
easy to do by calling undef_method
. There are some
methods that shouldn't be undefined: method_missing, of course,
and fundamental methods that begin and end with double underscores.
Here then is the base class for higher order messages:
class HigherOrderMessage def HigherOrderMessage.is_vital(method) return method =~ /__(.+)__|method_missing/ end for method in instance_methods undef_method(method) unless is_vital(method) end def initialize(handler) @handler = handler end end
I can then easily implement higher order message types by extending the HigherOrderMessage class and defining method_missing.
"Do" sends the captured message to all elements of a collection and returns nil:
class Do < HigherOrderMessage def method_missing(id, *args) @handler.each {|e| e.__send__(id,*args)} return nil end end
"Where" selects elements of a collection for which the captured message returns true:
class Where < HigherOrderMessage def method_missing(id, *args) return @handler.select {|e| e.__send__(id,*args)} end end
I can then add these higher order messages to all enumerable objects by adding them to the Enumerable mix-in:
class Enumerable def do return Do.new(self) end def where return Where.new(self) end end
And that's it for the basics. I added more classes for the
other higher order messages, and had to chain two higher order
message objects to support the having
predicate, but
nothing more complex than that.
Anybody got any ideas about an equivalent in Java?
Update: The code is available on RubyForge in the Homer project for anybody who wants to play with it.