MOP-ing Up with Herbal Cialis

by James Britt

So here’s the spiel: Object-oriented programming is all about sending messages.
See, given some object, you don’t directly call methods; oh no, you send messages, like asking a favor; “Hey, object, do you think, like, you could, ....”, and the object gets to decide if it wants do you the favor or not.
As a practical matter, the distinction between methods and messages varies among OO languages. For example, with Java, code using the standard message-invocation syntax will only compile if the message directly corresponds to a method.

foo.someMessage() // Compiler says the class behind foo must have a someMessage method

In Ruby, on the other hand, you are free to send pretty much any message you choose; the receiver does not need a message-method mapping in order to handle it.

foo.some_message # Code behind foo may or may not have a some_message method.

In the extreme case, you can have a Ruby class that handles all messages:

But even though Ruby affords this complete disconnect between messages and methods, most people code with the view that messages map to methods, or some well-defined set of message-based behavior. Maybe because coding pushover classes opens one up to too many risks. (But note that, in the right hands, you get great results .)
I wonder, though, if part of the reason is the syntax itself, coupled with the phrase “object-oriented”. Alan Kay, creator of Smalltalk, has said .

Again, the whole point of OOP is not to have to worry about what is inside an object. Objects made on different machines and with different languages should be able to talk to each other—and will have-to in the future. Late-binding here involves trapping incompatibilities into recompatibility methods—a good discussion of some of the issues is found in [Popek 1984].

In fact, you can’t even think about sending a message without first having an object.
Or can you?
Let’s see. First, we’ll need some new syntax. I’m not yet loopy enough to go invent my own language, so we’ll munge up Ruby code for our purposes. So this may not look as slick and clean as we might like. But, instead of the familiar

receiver.message( arg1, arg2 )

we’ll use flip things around a bit , and use

:my_message.>>( receiver1, receiver2 )

What this syntax means is, “Send the message my_message to all of the listed receivers.”
And this

:my_message[ arg1, arg2, arg3 ].>>( receiver1, receiver2 )

means the same thing, but also passing arg1, arg2, etc. as message arguments.
Oh, and before you wig out over funky syntax or the violation of the sanctity of Symbols or countless other details, know that much of what drives me to code (or do much of anything, for that matter) is an attitude of, “Gee, what happens if I push this button?”
So there’s an element of the, um, unpolished and experimental in the mix. (Though I do expect that admissions of this sort merely prompt many readers to leer and start rubbing their hands as the imagination revs.)

Message-oriented Programming

What I wanted to explore was some form of message-oriented programming, where one could start with a message, then decide on a set of receivers. I thought of different ways to write this, and settled on using Symbols because I wanted something that afforded a decent literal syntax (e.g., “my string”, [ :my, :array ], 123). The idea of having to instantiate an instance of a Message class before using one seemed clumsy. (String literals were my first choice, but having to type all those quote marks got tiresome, and it tended toward the fugly side of things.)
Symbols are also handy because they don’t actually do anything. There have very few methods, and are unlikely to pop up in unexpected places where the results of a classotomy might cause conflict. Adding new methods to the Symbol class seemed reasonably safe.

I picked the double-angle syntax as something of a visual indicator of transmission. My original version used block notation for passing arguments; this followed the list of receivers. But while writing this I decided it didn’t look very good, and, more important, didn’t seem to correctly convey its role. So I added the [] method. The basic goal was to have something that made some sort of visual sense. YMMV, blah blah blah, this is an experiment.
Syntax aside, the neat stuff is what happens inside Symbol. In the basic case, something like

:message.>>( recv1, recv2 )

would be translated somehow into

recv1.send( :message )
recv2.send( :message )

(And I of course if there are any arguments involved they’d get passed along, too.)
Of course, this raises a new issue: what is the return value, if any, of a cross-receiver message dispatch? I picked an array (and we’ll be getting to implementation details shortly). So, send a message to some number of receivers, and get back an array of response values.

Shooting in the Dark

I also wondered about possible use cases. If you already have a list of known receivers handy, then you might simply write a loop to do the message sending. But what if you wanted to send a message to some unknown set of objects, a set where the recipients are defined by some property or behavior? For example, imagine your application has all sort of IO-like objects floating around. You have no obvious way to locate them all, but you’d like to tell each of them to close, perhaps in preparation of some shutdown command.

My approach is to to allow the list of receivers to include Proc objects. Each Proc would need to define a conditional to determine if an object should be included in the receiver list. If, while processing the >> command, a Proc is encountered, the code loops over all objects in ObjectSpace, using the Proc to determine if an object qualifies as a receiver.

Pass me the MOP, please

So let’s see an example. Earlier we looked at dynamic code that might a bit too easygoing. Now let’s look at some really gullible code:

class Gullible
def initialize( name )
@name = name
end
def herbal_cialas
"Sure, I respond to herbal cialas!"
end
def bank_account_details
"@name: #{@name.intern.to_i}"
end
end

Suppose then that there are a few gullible objects floating around:

jim = Gullible.new( 'Jim' )
greg = Gullible.new( 'Greg' )

Now, if we decided to engage in some object-level spamming, we could really, um, mop up; we don’t need to know who these poor souls are, we just need to go find all objects that say they respond to messages about, say, herbal cialas, and ask them a favor, such as “Give me your bank account details”.
Like so:

7 Comments

I wonder if this kind of syntax could be used to implement multiple dispatch in Ruby?

Steve Yegge
2006-01-05 19:50:14

Brilliant! And very nicely written, too.

I'll never write another object-registration class again. If only I could find a fast way to port my 500k lines of Java code to the (no doubt) 80k lines of equivalent Ruby code. You have no idea how many memory leaks this trick would have saved me. I had to mop them all up by hand.

James Britt
2006-01-05 20:21:05

Brilliant! And very nicely written, too.

Thank you.

I'll never write another object-registration class again

I've not used this in any production code, but it seems that testing *every* object to see if it should be a message recipient would get expensive in large systems. But maybe not; might depend on the efficiency of the Proc test. And maybe speed isn't as important as simplicity or clarity of code.

Daniel Eklund
2006-01-09 11:52:31

Ruby is the gateway drug to CLOS.

Nice article. I wish I'd see more like these.
The dynamicity [sic] of Ruby is quite impressive.

2006-01-11 19:45:20

Perhaps MOP should stand for 'Massage Oriented Programming'.

You 'massage' the object, until it's relaxed enough to take a message it didn't expect.

Either way, nice write-up.

Sign up today to receive special discounts, product alerts, and news from O'Reilly.