Class Warfare

* - *
|

I'm as much of an OOP adherent as the next person. You say to me: "encapsulation, polymorphism and inheritance" and to you I say, "right on." But I find it interesting and somewhat disappointing that the notion of "object-oriented" is conflated with the notion of "class", which I consider an implementation detail.

I'll get back to some of those implementation details in a second, but let me lay out the two reasons why I don't like classes.

First, classes are too big to be truly atomic. If you consider the spectrum between a single function or data type, and the whole of a program, classes land very near to the latter. Every working programmer knows this problem, and there are any number of informal and formal strategies for dealing with it. Aspects recognize this problem, but I find the Aspect notion complicated, too tied to the particulars of the Java language (even though it's not supposed to be,) and in practice, only useful for a limited number of applications.

The second problem with classes is that they don't allow very well for the fact that objects change over time. If I take a UI element in my program and drag it from one window to another, it's likely that some fundamental aspects of what it is will change at the end of that operation. As a class designer, though, I have two unappealing choices when specifying the class. One is to write a single class that encompasses all the possible behvior that the object could have (along with the requisite switch statements.)

The other possibility is to write several classes to express the various possibilities for its end state, and then to write the n! constructors for those classes so that I can create an instance of any once class from an instance of any other. I could try to solve some of this problem by giving all the possible end-states a common superclass, but then I have to choose either only to represent the particular object as a class hierarchy, or to represent the abstract notions of 'draggable', 'inwindowA', and 'inwindowB' as classes, but not both. No matter what else happens, I'm going to have to destroy and re-create the object, which is likely to be slow.

Multiple inheritance was supposed to solve some of this problem, but it has become something of a dirty word in modern programming with the advent of Java and C#. This is not (in my opinion) because multiple inheritance wasn't a useful concept, but because it was too hard to use and because it leads to all sorts of unexpected behaviors and errors. This is my thesis statement, tucked into in aside: I'm basically saying that classes don't really do a good job of representing abstract things at all -- they ultimately always must represent instances -- which is antithetical to the highest aspirations of OOP.

Now it may seem strange to criticize classes and endorse OOP, but in fact the two are almost completely separate. Classes make it easier to write compilers, and (arguably) easier to write interpreters, but otherwise, they're pretty 20th century. Consider this snippet of Javascript:

    function f (){ Debug.write( "My name is" + this.name ) };
    var o = { name : "Object o" , sayName: f };
    o.sayName();

That's encapsulation. Here's polymorphism:

    function g (){ Debug.write( "I don't have a name." )};
    var n = { sayName: g };
    list = [ o , n ];
    for ( var slots in list )
        list[ slots ].sayName();

Note that we've done all this without creating a template for these things. The call-by-name semantics of Javascript, combined with its first-class functions, make this possible.

Now inheritance is trickier. I've written about __proto__ before, and this is where it comes into play. I could add a method to both o and n by giving them the same __proto__ and then manipulating that.

    var sup = {};
    sup.enumerate = function {
        for ( var keys in this ){
            Debug.write( keys, this[ keys ] );
        }
    }
    o.__proto__ = sup;
    n.__proto__ = sup;
    o.enumerate();
    n.enumerate();

Note that there are some important differences between __proto__ and classes. The first is that __proto__ is a post-hoc operation. I can set and change my __proto__ well after I've been created. Second, my __proto__ can be an instance of an Object. This gives me several powerful options, like transforming a set of objects by calling a function on their __proto__, and it gives me a fast way to clone an existing object. It also lets me do exotic stuff, like configure a class at runtime.

This is a side note, but it's worth taking a second to think about the fact that runtime-interpreted languages have a curious property. Terse syntactic constructs tend to run faster than longer ones, because in general, most of the time spent running a program is in reading and dispatching the bytecodes. This essentially means that the more power in the constructs that are built into the runtime, the faster its programs can run.

Anyway, Javascript isn't perfect. __proto__ solves problem #2 above (the fixed-ness of classes), but not problem #1 (the bigness of classes.) My big complaint with __proto__ is that it is a single slot. This makes multiple inheritance hard to implement and much less efficient than it could be. What I really want to be able to do is to define a set of very small atoms which can by combined in arbitrary ways. What follows is not real syntax in any language, but hopefully you'll get the idea:

    Draws = {};
    Draws.draw = function (){ ... }
    Draws.invert = function (){ .... draw() .... };
    ...

    LoadsMedia = {};
    LoadsMedia.load = function ( url ){ ... };
    LoadsMedia.draw = funtion () { ...super.draw()... };

This is very similar to the inheritance pattern, but with an important difference, because I could do this:

    View = new LoadsMedia,Draws;
    aview = new View;

Here's another thing I could do

    Minimized = {};
    Minimized.draw = function(){ /*make smaller*/ ... super.draw() .... };

    aview.pushProto( Minimized );
    aview.draw();

I think you can imagine what I think pushProto should do.

The big ? (read "question mark") in the above code revolves around the exact semantics of "super." I have some ideas about that, but I think I've gone on long enough for this particular installment. Hopefully, I'll get around to treating that at a later date.

I've been calling these little objects that represent parts of classes "transformers." People laugh at me, but I'm used to it. As usual, I've written some code that shows what you get from Javascript.

VIEW/EDIT PROGRAM

<canvas debug="true" height="250">
    <debug height="240"/>
    <script>
        function f (){ Debug.write( "My name is" + this.name ) };
        var o = { name : "Object o" , sayName: f };
        o.sayName();
        Debug.write( "---------" );

        function g (){ Debug.write( "I don't have a name." )};
        var n = { sayName: g };
        list = [ o , n ];
        for ( var slots in list )
            list[ slots ].sayName();
        Debug.write( "---------" );

        var sup = {};
        sup.enumerate = function (){
            this.sayName();
            for ( var keys in this ){
                Debug.write( "   " +keys+ ":", this[ keys ] );
            }
        }
        o.__proto__ = sup;
        n.__proto__ = sup;
        o.enumerate();
        n.enumerate();
    </script>

</canvas>

Comments

Your 'transformers' are called 'mixins' in Flavors (an early experiment in object-oriented programming in Lisp, ancestor to CLOS and Dylan and inspiration for some of the ideas in C++). When you think about it, they are really just abstract classes, and what makes them abstract is that they are really just fragments of DNA, they can't be instantiated by themselves -- they need a host.

I don't think multiple-inheritance was left out of C++ (and hence Java and C#) for any reason other than Bjarne thought it would be too slow to implement in cfront. He as much as says so in "The Design and Evolution of C++". Implementing multiple-inheritance correctly and efficiently is a challenge (but less so when you are creating a virtual machine). Craig Chambers has a lot to say on this subject. He has a presentation "Efficient Implementation of Object-oriented Programming Languages" that he gives at OOPSLA or PLDI every so often. I keep hoping he will evolve it into a book.

Another book you might want to look into is Gregor Kiczales', et al. "The Art of the Meta-object Protocol". This is a great book on how to think more abstractly about OOP. CLOS actually implements a lot of this, making it a great testbed for experimenting with different OOP approaches.

My first comment! How exciting!

There are two ways in which transformers (as I imagine them) are different from mixins (as I understand them.)

1) I imagine that transformers could be applied to and removed from an instantiated object -- essentially making it possible to a change an instance's class at runtime.

2) Although most transformers would be abstract, they wouldn't have to be. I imagine that I could clone an exisiting instance by just calling new with it.

"Art of the Meta-object Protocol" looks great. Thanks for the recommendation.

I recommend Henry Lieberman's article "Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems", at http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.htm....

I found this from a link from the Prothon site . Prothon is a classless prototype-based version of Python.

|
* * *