Periodically, people try to distill the essence of object-orientation. They come up with rules of thumb that pack a powerful punch. One of these is “Tell, Don’t Ask.” The Pragmatic Programmers came up with that phrase and it’s a great piece of advice. When possible, prefer to tell an object to do something rather than asking it for some data. If you ask an object for some data, massage it, and then pass it back to the same object, you’ve found an operation which probably belongs on that other object.

Rules of thumb are great, and no one expects them to apply every place, but I’ve seen some interesting things when I’ve tried to push “tell, don’t ask" to the extreme. I end up with pipelined architectures. Data comes into the system and it’s processed by an object. The object sends a message to another object, and eventually you end up with some event or action that is produced by the last set of objects in the call chain. This sort of design works, but it’s definitely not the way you'd tackle most design problems unless you're working in shell.

I was reminded of this sort of architecture a while ago when I was looking at some code that Steve Freeman and Nat Pryce have been using as examples in their JMock2 tutorials. A common theme around their work is that correct use of mocks in test-driven development can lead to a rather different style of design. Rather then telling an object to do something and then asking whether it was done, you tell an object to do something and then see what happens to its collaborators. This leads to designs that are somewhat like what I described above. They are a bit like a workflow. In conversations about larger systems, Steve tells me it’s better. There’s less design impact under new requirements. And, it makes sense. Each object is responsible for doing one thing and notifying its successor. That’s a bit less than what you have when your objects are responsible for doing things as well as coordinating interaction with objects that give them results.

No, this “push” architecture is a bit different. It’s almost the opposite of functional programming. In the purest form of functional programming, you never tell; you ask. And when you have lazy evaluation, the system only does as much as it really needs to when it’s answering your question. But, despite the differences, there are similarities between these “push” and “pull” systems. Both of them seem to have a relatively stateless substrate that data flows along.

So, there’s this confluence between “tell, don’t ask” and a particular approach to TDD. What’s happening here? I think it comes back something central. Return values push work back on callers. It's not enough to do some work and decide to send a message to a collaborator; you have wait for something to come back to you and then, perhaps, do some more work. We're better off not returning values.

We can carry it a bit further also. If we aren't returning values, maybe we're better off sending asynchronous messages send rather than synchronous messages? If we aren't waiting for a return values, why not?

Curiously enough, this model is somewhat like the one used by Erlang. Erlang has developed quite a buzz recently. It presents a concurrency model that is almost entirely different from the norm. In Erlang, creating new processes is cheap. It might even be cheaper than creating objects in OO languages. The idea behind Erlang is that if you can make a large number of processes and guarantee that they never share state, you can develop more robust systems. Each process receives messages, does its work, and sends messages to other processes. The message sends are largely asynchronous. Sounds familiar, doesn’t it?

Ralph Johnson has written that Erlang is really OO at its core. Processes are objects. They are little processing hubs that do work and send results to their neighbors. In a way, this is much closer to the metaphor of objects than we’re used to. Alan Kay has mentioned that messaging is primary to OO, that the metaphor is cells in an organism. Cells send chemical messages to other cells, and I’m no biologist, but I doubt they are synchronous.

I nearly didn't post this blog tonight. It's terribly abstract, and it sort of flies off in several directions. Seriously, I can only think about this sort of thing for so long before I'm ready to knock myself in the head just to reconnect with something tangible, but it is nice to mull some of these things. You never know what you'll end up with.

If you have time, try the extreme "tell, don't ask" experiment. Try to write a little system using only methods with no return values. If you do, ask yourself afterwards how you really decide whether to ask or tell in your designs. There's a lot to learn there.