Tuesday, March 24, 2009

Erlang creator Joe Armstrong is no fan of OOP. He says it sucks! Oof! As someone who is building an object oriented language on a platform pioneered by Joe, I feel I'm at least obligated to respond to his concerns. Due to its strong Erlang influences Reia's object system ends up working a lot different than most other OOP languages so in some ways this does address some of his concerns.

Let's look at them:

Objection 1: Data structure and functions should not be bound together

So first, a confession: Reia's "builtin" types (which I'll get to later) are guilty of this. So -1 on my Joe score.

However, Reia's objects are more than just data structures. This is certainly a complaint about OOP which holds for most languages: "object" is really a fancy word for "a big chunk of state" and sending an object a "message" is really some more high falloting verbeage for "making a function call". In this regard OOP ends up seeming pretty imperative: it's just a big fancy facade for calling functions which probe and mutate state.

Similar concerns were noted by Clojure author Rich Hickey in his article On State and Identity. He notes that in most languages objects don't have real identities beyond their state and that OOP at its core is very much an imperative affair.

In Reia, all objects are Erlang processes and operate concurrently. Each object has a life of it's own... it's not just some lifeless chunk of data sitting somewhere in RAM, completely at the mercy of whatever modifications some random thread may throw at it.

Calling a method is more than just a function call. It uses messages... not goofy metaphorical messages but real, first-class messages. Presently Reia's objects are built on gen_server, so invoking a method on an object performs an RPC on the remote object.

Self-ascribed OOP creator Alan Kay is practically obsessed with messages. He once wrote: "I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages (so messaging came at the very beginning -- it took a while to see how to do messaging in a programming language efficiently enough to be useful)." What he began with messages in Smalltalk would eventually be boiled down to function calls in most successor OOP languages (with C++ being perhaps the most infamous example).

Reia does not follow in this tradition, but maintains a very message-centric view of OOP which is more akin to what Alan Kay originally had in mind. I've also read that Alan Kay has claimed that not making objects concurrent was one of his greatest mistakes, although I don't have a specific citation on that one (if someone knows please leave me a comment).

Erlang vis-a-vis the actor model and its outgrowth from Smalltalk certainly embodies similar concepts.

Objection 2: Everything has to be an object

Well, this is an easy one to answer: everything doesn't have to be an object! I'm sure any Rubyist readers are groaning on that one. Pretty much every language I see coming out of other Rubyists follows this paradigm. Reia doesn't.

Reia has three fundamental types of entities: builtins, processes, and objects. Objects can be thought of as a type of process, but all objects respond to a common base set of messages. Reia's objects can thus address a particular concern of Rich Hickey's about actor-based systems:

"You can't observe anything without its cooperation/coordination - making ad-hoc reporting or analysis impossible, instead forcing every actor to participate in each protocol."

Reia implements an inheritance system, which means that you can group the protocols that you want a given set of objects to respond to together into superclasses. All objects will, at the very minimum, respond to the set of methods defined in the Object superclass.

You can certainly do the same in Erlang by placing common functionality into a "superclass" module. Reia just gives it first-class syntax and does some of the thunking a bit more automatically.

Reia also provides processes, which are for all intents and purposes identical to Erlang processes. For some reason many OO purists have messaged me asking if this is necessary and suggesting I rip it out. Reia does provide asynchronous features as part of its object system... it has (or rather, will have) equivalents for gen_server:cast and gen_server:reply. Given this, they argue, who needs processes?

I think these people don't understand that gen_server is not a one-size-fits-all solution. Since I'm building a language on top of Erlang it would be silly not to expose the full power of its messaging system and try to force everything into gen_server-shaped boxes. Reia goes the opposite route and provides processes which are virtually identical to Erlang. Reia even has the same "!" syntax for sending messages, and it works on objects as well as processes.

Reia's builtin types are made out of plain old immutable Erlang terms. They are not objects themselves. With a little bit less "throw objects at the problem" they can serve to represent all kinds of things. My hope would be programmers use builtins more and objects less than they would in a typical OOP language.

Objection 3: In an OOPL data type definitions are spread out all over the place

Guilty as charged... take another point off my Joe score. Next!

Objection 4: Objects have private state

Also guilty. However, I hope Reia gets some bonus points in this regard for the way it implements its private state. Behind the scenes Reia's representation of "instance variables" is a pure functional one. The compiled form has the "instance variable" state (represented as a dict) enter a method (now a function) as an argument. The state is versioned throughout the compiled form, so wherever there is potential for change it's bound to a new label. When the method completes the final state is returned.

Because this state is represented in a pure functional form behind the scenes it means Reia retains some of the benefits it brings in Erlang. For example: private state is transactional, as the object's true state isn't actually updated until method dispatch has completed, in the same way a gen_server's state isn't updated until one of the handle callbacks has returned with the new state.

It also means the scope of private state is limited. Any cases I've encountered (such as lambdas) where I cannot implement state modifications in a simple, pure functional form I've avoided it. Lambdas are able to access the private state (which they close over at the time they're declared) but they are not allowed to modify it.

This approach has been more complex than alternatives like just giving the process dictionary a first class syntax. I could've done that. However, compiling to a pure functional form really helped me see the corner cases where the state was trying to creep into strange scopes. The current implementation has precise semantics about state flow and works in a way not too far distant from how an Erlang programmer would actually write it.

What's the point?

I think that question is pretty much the sentiment of Joe's article. Is OO actually useful? Given certain technical drawbacks (i.e. side effects) introduced by hidden state it becomes more difficult to make the same statements about a Reia object that you could about an Erlang gen_server (and destructive assignment doesn't help things). Is there actually some value objects bring to the table?

The merits of objects aren't technical, in my opinion. I think they're a good model for certain types of people to conceptualize problems. Lots of people really like them. Joe tried it and said it felt "wrong" but for many it does feel "right" in some way.

Fortunately, for the people who think it feels "wrong" there's this great language called Erlang you can use...

Tony, since you brought up Rich Hickey's essay on state, do you have any thoughts on the effects of 2-message blocking reads and the serialization of reads and writes on same-process efficiency and protocol design?

"I've also read that Alan Kay has claimed that not making objects concurrent was one of his greatest mistakes, although I don't have a specific citation on that one (if someone knows please leave me a comment)."

According to Alan Kay, the key is messaging – not classes or objects. He regrets not implementing messaging in Smalltalk/Squeak as he intended. This is what he had to say about objects and messaging:

Smalltalk is not only NOT its syntax or the classlibrary, it is not even about classes. I'm sorry that I long ago coined theterm "objects" for this topic because it gets many people to focus on thelesser idea.