TITLE: Turning Up the Knob on Functional OOP and Imperative OOP
AUTHOR: Eugene Wallingford
DATE: January 27, 2011 4:48 PM
DESC:
-----
BODY:
One of my students has been learning Haskell as a prelude
to exploring purely functional data structures. Recently,
he wrote a short blog entry describing some of the ideas
he has found most exciting. It ended with a couple of
code snippets showing the elegance and brevity of list
comprehensions compared to what he was used to in
imperative languages. The student apologized for his
imperative example, because he wrote it in Java using
objects. In his mind, that made it object-oriented,
not imperative.
This is a common misconception. Most OOP is imperative.
Objects have state that changes.
Of course, one can write object-oriented code in a
functional style by emphasizing return values and by
creating new objects instead of changing existing objects.
Certain kinds of objects, such as money, should probably
be implemented as
value objects
without modifiable state. But most OO practice and intent
is stateful, hence imperative.
I've read many a blog entry over the last few years in
which OO gurus extol functional OO as a way to write better
code. I think we can overdo it, though. Whenever I take
this idea too far, though, I soon find myself contorting
the code in a way that seems to serve the idea but not the
program I am writing. Still, sometimes it can be fun to
turn the knob on the functional OO dial up to 10 and try
to write purely functional OO code, with no side effects
of any sort. This kind of
programming challenge
has always appealed to me. It can teach you a lot about
the strengths and limits of the tools you use.
It occurs to me that one way to enforce the rules of the
functional OO challenge would be to turn off the imperative
features in my language. That can be tough to do in a
language with libraries full of stateful objects. But
simply turning off the assignment operator in a language
such as Java would make many of us struggle to write even
simple programs.
Actually, I had the idea of turning off assignment
statements late in a long conversation I had with myself
while thinking about my student's comment and my response
to him. If most OO is imperative, I wonder what it would
be like to write "purely imperative" OO code. This would
mean creating objects that never returned a value in
response to a message. In a sense, these objects would
be pure state and action, at least from the perspective
of other objects in the system.
At first, this idea seemed absurd. What value could come
from it?
This stylistic challenge is quite easy to enforce, either
in practice or in tools: simply require all methods to be
void. Voilé! No return
statements are allowed. No values can be passed from one
object to another in response to a message. An object
would affect the state of the program either by modifying
its own state or by sending a state-changing message to
another object, perhaps an argument that it received along
with a message.
Talk about
Tell, Don't Ask!
In this style of programming, I can only tell
objects to do things. I can't ask for any data in return.
So, perhaps some value could come from this little
challenge after all. I would have to take Tell, Don't
Ask -- and encapsulation -- seriously. Programming in
this way can help us see just how much we can accomplish
with truly independent objects, providers of services
who encapsulate their state and take full responsibility
for its management. I think that, in many respects,
this idea is faithful to the original idea of objects
and OOP -- perhaps more faithful than our current
incarnation of them in languages with functions.
I think that this could also help us in another way.
Functional programming offers us one path to increased
parallelism by eliminating state changes and thus making
each computation independent of global context. Purely
imperative programming offers another path, one that
fits the early OO vision of encapsulated agents
interacting via message passing. This is similar to
the
actor model
that we see these days in languages such as Scala and
Erlang. Of course, this model goes back to the work
of Carl Hewitt, which inspired the evolution of both
Scheme and Smalltalk!
I have not thought through all the implications of my
thought experiment yet. Maybe it's nonsense; maybe
it's a solved problem. Still, I think it might be fun
to turn the dial up to 10 on stateful programming and
try to implement a non-trivial program with no
return statements. How far could I go
before things got uncomfortable? How far could I go
before I found myself contorting the code in a way that
serves the idea more than the program I was writing?
Sometimes I am surprised just how many interesting
thoughts can fall out of the simplest conversations.
Too bad time to play with them doesn't fall out, too.
-----