Object-oriented programming

March 30, 2007 at 2:16 pm

Non-geeks beware, the following is a rant about programming. Programming is the freaky voodoo thing that we nerds with thick glasses and greasy hair do in order to allow you to go on to MySpace and tell all your friends about what you did today. If you don't care about how we make that stuff work, just ignore this post.

The Problem

Object-oriented programming, as it exists (or at least is practiced) in the larger part of the software development world today is little more than procedural programming with some extra stuff to avoid naming collisions. There is a large amount of criticism against object-oriented programming which states that there is little if any benefit to it over procedural programming, and that criticism is well deserved with the way that things are today.

What's wrong with object-oriented languages today? First and foremost, there is one fundamental aspect to object-oriented programming that very few languages that claim to adhere to its philosophy seem to have gotten right:

Everything is an object.

C# more or less has that quality, but it doesn't do much to capitalize on it. C# also tends to handle some of the fringe elements of that trait poorly.

Java doesn't have that quality. The primitive data types are not objects. Java 5 adds a feature that will automatically box and unbox the primitive data types in certain contexts, but it's not the same thing.

I'm hardly going to honor C++ with a mention in this post. For now, I just want to leave this quote.

I invented the term object-oriented, and I can tell you I did not have C++ in mind.

Alan Kay

The obvious question at this point is, "what about 'everything is an object' is desirable?" The answer arises upon an examination of what the quality entails.

Consider the basic description of object-oriented programming: classes describe objects. An object is comprised of data and methods that act on that data. Objects communicate with each other via messages.

Any Java or C# code monkey would more than likely agree that the preceding description is accurate. Consider, however, what "everything is an object" means. That means that, in a true object-oriented environment, the classes are objects, the methods are objects, and the messages are objects. That means that classes, methods, and messages need to be able to be instantiated, modified, and passed between objects as parameters and return values. Most of the rest of this post will describe what happens when you can do that.

Classes

A class describes an object. When a class is also an object, things get really interesting.

Given a sufficiently sophisticated serialization system, a class that is also an object can be serialized, just like an object. This includes the methods, which means that if you want code to talk to another machine, you can not only submit data to it, but also instructions on how to work with that data.

If a class is an object that can be instantiated and modified like any other object, that means that you can create new classes and modify existing classes at run-time. Visual Studio 2005 introduced a spiffy feature called Fix and Continue. It allows you to modify classes while a program is running and see the results of your modification without restarting the program. That's a neat feature, but it's not terribly impressive when you consider that Smalltalk has had that for several decades. In fact, Steve Jobs saw it during his Macintosh-inspiring visit to Xerox. He didn't like an aspect of the scrollbars, and when he voiced a complaint, the demonstrator went to the code in question, modified it, and showed the awe-struck Apple employees the new behavior without restarting anything.

Now, both of those points are related to the fact that methods are objects as well, which brings us to our next topic.

Methods

When methods are objects, you are able to modify them at run-time, as mentioned before, but you're also able to lob them around as parameters and return values. The value of this is clear as day to programmers of functional languages like LISP and Haskell, but let me demonstrate by example for other programmers. What if you had, say, a list of numbers, and you wanted another list that contained all of the elements of the first list such that each element was greater than the number five. You could accomplish it like this in a traditional language.

var result = [];
for(i=0; i<source.length; i++) {
    if(source[i] > 5) {
        result.push(source[i]);
    }
}

Not terribly complicated, but look what can happen when you can pass a method as a parameter to a function.

var result = source.filter(new function(x) {
    return x > 5;
});

Quite a bit shorter, no? Now, I should probably explain how that code works. The filter method would be defined in the Array class, and it would take a function as a parameter. For every element in the array, it would execute the method, passing in the element as the parameter. If the result of the method invocation was true, then the element would be added to another array. After iterating over each element, the resulting array would be returned. A more terse language that I'm making up on the spot may define the same code thusly.

var result = source filter: [x -> x > 5];

Being able to pass methods as objects yields incredible power, and I've only scratched the surface, but now I want to talk about messages.

Messages

Most object-oriented languages out there get this totally wrong. If a message is an object, then its properties are the name of the method it wants to invoke (its selector, if you will), a pointer to the object it wants to invoke the method on, and a list of parameters. When invoking a method on an object, you don't jump on a function pointer, you create a message object. This message object is compared against the class object of the object that the method is being invoked on. If there's a match, then the method object is taken from the class object and executed, being passed the pointer to the destination object and the parameter list.

That may sound a bit odd, or a bit like something involving much redundant overhead, but it yields remarkable freedom. Basically, you can drop any notion of C#'s and Java's interface construct. In Java, if you want to respond to a mouse click event, you need to implement a MouseListener interface, add stubs for all methods in the interface (including methods you don't care about, like mouse move), then add yourself as a listener to some control. When you have the abstraction layer described earlier, on the other hand, you don't need interfaces. When the control wants to send out its mouse click event, it can simply query the class objects associated with its listeners, ask them if the objects respond to the mouse click event, and call the mouse click method only if the objects respond to them. The object that wants to respond to mouse click events need only implement the mouse click method. It does not need to say that it implements an interface, nor does it need a bunch of empty stubs.

One final bit about messages before I conclude. Like class objects, you should be able to serialize your message objects and ship them across a network. Imagine making a request for a web site via a normal method call, and having that call run across the internet to the server, which responds to what it sees as a normal method call by returning the website, which is serialized, shipped across the wire, unpacked and treated as a return value by the client.

Likewise, everyone knows that one of the coolest parts of X11 is the fact that all of its graphics commands can be sent over a network, allowing a computer to be miles away from its display, keyboard and mouse. When you can serialize messages, you can basically get the same functionality for free by intercepting all graphics messages, serializing them, and shipping them between two different computers.

Conclusion

This rant has been terribly long-winded, but it let me unload a lot of what I've been wanting to say for awhile. I don't like where I see the larger part of object-oriented programming today. All of what I've described should be incredibly easy to code, but instead it's incredibly convoluted and difficult, if not impossible, with the mainstream languages available today. I feel that things need to change significantly.

By the way, I would just like to say that I'm in love with my MacBook. I wrote this whole thing while listening to music over several hours with my screen at maximum brightness, and I still have about an hour of battery life.