26/03/2001 : re-edited according to input got on Kuro5hin

This draws heavily from a post of mine on Advogato, following up this article.

Re-evaluating the "OO in any language" myth

A very common claim regarding OO programming is that it doesn't actually require an OO programming language. While this is true in principle, there are other important factors to consider. This article attempts to show why, even if you can implement OOP with any language, claiming that an OOP is just syntactic sugar is absurd. For this I try to show what drives the evolution of programming languages, what are the drawbacks of implementing complex programming features "by hand", and apply this to the specific case of OOP.

Language evolution

Languages have always been evolving towards higher levels of abstraction, (assembly, C, C++, Java / C# - see this article). As a language is used, people start expressing similar patterns, or styles, which evolve into a fully fledged programming paradigm (structured programming, OOP...). Usually the need for a new language directly supporting this paradigm appears, because it will be more efficient on all counts : the language gains in expressiveness, so the programmer has less to do to make himself understood and will write faster, with less mistakes.

A side note: you can recognize that a new programming paradigm has appeared to two unmistakable signs :

Therefore claiming that you can use such a higher-level paradigm just as efficiently without direct language support is basically reversing the very process which brought these features into the language in the first place. You will of course gain from implementing a paradigm in a language not natively supporting it (provided, obviously, that it fits your needs in the first place), but it cannot be as effective as directly using a language which does. I'll try to show why.

Features through syntax

In general, implementing "large" features through syntax conventions works, but it's mostly up to the programmer to enforce the required code structure, instead of the compiler. Some actually like it and say "so I just have to be careful, big deal", or "we've got added flexibility". However, in practice what seems like "a little bit more verbosity" quickly gets in the way of the readability of the code. This Advogato diary entry from Nathan Myers puts it better than I could.

Another side note: Emotion usually prevails on factual reasoning when appreciating a programming language. If you like a language, you will happily suffer through its quirks and shrug at them, and each of its features will look absolutely essential. If you don't like it, you will consider its features useless or outright bad, and even the tiniest oddity will look fatal.

See this interview for a similar reflection on features through syntax, taking object properties as an example :

"But going back to these key component concepts, there's been a lot of debate in the industry about whether languages should support properties or events. Sure, we can express these concepts by methods. We can have naming patterns like a "get" block or a "set" block that emulate the behavior of a property. We can have interfaces and adapters that implement an interface and forward to an object. It's all possible to do, just as it's possible to do object-oriented programming in C. It's just harder, and there's more housekeeping, and you end up having to do all this work in order to truly express your ideas. We just think the time is right for a language that makes it easier to create components."

The case of OOP

Implementing features in a language stems from the same reasoning process which makes you write a function to wrap an often-recurring sequence of actions. Or more generally which makes people create new words for new concepts as soon as that concept is used often enough. We don't say "portable tape player with headphones", we say "walkman", it's more efficient and easier to understand. Likewise, it's more efficient to type

class Foo : public Bar { ...  }; 

or

class Foo extends Bar { ...  }; 

than

typedef struct Foo {
 Bar _bar;
 ...
}

and then having to do all the OO housekeeping work instead of letting the compiler do it for you. OOP brings new concepts, so it's logical that the language should let you use them directly, instead of forcing you to do so through their underlying description.

Ease of use and scalability

The other problem with the feature-through-syntax approach, aside of the added strain on the programmer, is that however well done the framework is, it's heavier than the equivalent feature built in a language. This means both mem/cpu and plain code typing overheads. And a programmer is very unlikely to use a feature if it's not easy to get working.

Programmers are lazy by nature, programming is about being lazy. It's about getting the machine to do as much of the job as possible. So if a tool is too complicated to get working, it won't be used unless absolutely necessary, and cases where it "should" be used will be routed around. Because of this, a programmer is much less likely to create classes in a non-OO language with an OO framework added than in an OO language. Typically, he will end up having a relatively few number of very large classes.

Also, such a framework tends not to be very scalable both up and downwards. To implement things like OO Design Patterns is quite hard. To implement very small classes like a string or vector class (which are huge assets in a programmer's toolbox) is not worth it, because of the framework's overhead.

Another scalability issue is on the number of programmers using the framework. It will up to them to be "more disciplined" and perform checks that the compiler otherwise would. So when this number increase, so will the number of bugs due to mistakes in using the framework itself.

Taking a step down the abstraction ladder : a theoretical example

To further illustrate my point (especially to C buffs :-), suppose a language called C-- which has only if, goto, gosub and return keywords, no loops, no functions, and only global variables. Then someone says he has implemented loops and functions through a framework of macros and syntactic conventions on which the compiler can't perform any checks. It's just up to you to be careful, and to do all the typing, something like


/* This is a function */
MY_FUNC:
/* get arguments */
int my_func_local_1 = int_stack_1;
int my_func_local_2 = int_stack_2;

/* return results */
int my_func_res = my_func_local + my_func_local_2;
return;

/* ... a function call : */

PUSH_ARGS(12, 13);
CALL_FUNC(MY_FUNC);
/* do something with my_func_res */

Now would you consider using it rather than C ?

I believe that even if C was less portable than this imaginary C--, and would produce bigger executables, most of us would still take C any time :-).

28/03/2001 : Much to my surprise, it turns out that C-- actually exists.


Guillaume Laurent
Last modified: Sat May 19 16:01:59 CEST 2001