Today I read Analysis of Inheritance Anomaly in Object-Oriented Concurrent Programming Languages, and it seems that the idea that I had last week about implementing every object instance as it’s own process has already been considered. As I expected, this attempt to marry principles of inheritance and polymorphism within a framework of concurrency is fraught with problems. Most of the problems are related to state. It’s relatively easy to make each method a ‘critical region’ so that methods are run in sequential fashion (queued in a message box) so that they don’t step on member variables, thus making state transitions atomic. This isn’t enough.

Matsuoka and Yonezawa identify a few cases where inheritance can cause rather difficult problems.

  • Partitioning of Acceptable States
  • wherein what was considered a single state in the base-class is now divided into 2 or more states in the subclass. Their example being a sub-class of a buffered queue that introduces a method get2 that will return 2 items from the buffer. This partitions the base class states of {full, empty, partial} to {full, empty, twoOrMore, one}. The new states require re-implementation of the base methods get and put in order to track the availability of the get2 method.

  • History-only Sensitiveness of Accptable States
  • wherein the subclass introduces a new method gget that is dependant on the object’s history (it can only be called if the last method called was put). This requires the addition of an instance variable after-put that will keep track of the necessary state-history. Because of interactions with base-class methods, all methods will have to be re-implemented. (put must set after-put = True while all other method must set after-put = False)

  • Modification of Acceptable States
  • wherein the subclass introduces new methods lock, unlock that adds to the states already available in the base-class. So that once the object is locked none of the methods can be accessed until the unlock method is called. This would be most useful as a mixin class. It still requires the re-implementation of the base methods, even if guards on the methods are used.

The paper talks about some of the solutions that have been tried (guards on method calls, explicit state availablitiy (Rosette), Path Expressions, Explicit Method Reception, etc..) The authors rightly conclude that

…in order for OOCP languages to be usable for large-scale programming, the inheritance anomaly needs more thorough theoretical analysis, plus derivation of a good solution. We need to strive on the following:

  • Establishing a more precise and formal definition of classification of inheritance anomaly. …
  • Further identification of a general class of synchronization schemes with respect to anomaly classifications. …[Categorization and study of all the inheritance and state interactions]
  • Proper development of languages (features) that either totally avoid or minimize inheritance anomaly, based on the above two analysis.

It is my opinion that most, if not all, of these problems stem from what I think is the bane of concurrency models: mutable state. I really think that the object-oriented paradigm has incredible utility (esp. in GUI), and I’ve fully embraced thinking in OO terms. Now, as I descend more into the concurrency and functional world (through Erlang) I’m discovering exactly how much my mind depends on the imperative patterns. In my experience, banishing mutable state is going to be the only way to write concurrent programs that will scale naturally. Though I’d still like to bring along inheritance, poly-morphism, and mix-ins to the world of concurrency. The marriage off these two paradigms is going to be difficult to reconcile.

Without mutable member, OO programming will look very different. Each method will be force to return a new object that encapsulates the change in state. This does however, simultaneously help solve the Circle-Ellipse issue. But it just seems inefficient. We want to think of the object as having an internal, mutable state. Perhaps the compiler or interpreter can do some behind-the-scenes optimization.