Chapter 2. Object-oriented oblivion

Object-oriented programming languages are a major misstep; a tortuous detour that computer scientists should never have taken. It’s not all bad, as some problems are handled extremely well by class hierarchies. However, others, such as the the circle-ellipse problem, are not.

The limited one-size-fits-all philosophy of some object-oriented languages makes coding cumbersome. I’m annoyed at myself because I was a crusader for the object-oriented empire for years; I thought all data could be forced into an elegant hierarchy if I had sufficient skill, and everybody not using objects was creating unnecessary disorder.

Besides the strait-jacket type system, my other big complaint is the second-class status of functions. Have language designers learnt nothing from functional programming? Good notation for functions and closures allows otherwise clumsy code to be expressed clearly and concisely.

When I must use a object-oriented language, I shun inheritance, and struggle to obtain the equivalent of simple C constructs such as function pointers. Although I can work around most of the deficiencies, I feel like I’m fighting bureaucracy and wrestling with red tape. Instead of conversing with the machine and fellow programmers, I’m filling in forms the compiler gives me.

Hackery or quackery?

Programming is hard. Brian Kernighan said: “Controlling complexity is the essence of computer programming.” This was probably known even before computers were invented.

I theorize that over-enthusiastic researchers spread a rumour that object-oriented design would simplify programming around the 1980s. It must have been easy to believe, because anyone would want a magic pill to soothe their complexity pains. They promised you could clean up systems by reorganizing them into class hierarchies. If not, then you’re just looking at the problem incorrectly: try shuffling around the classes. Or apply a cool design pattern with a cool name.

Many programmers were caught in the madness. Suddenly, printf("0x%08x\n", x) is less clear than:

std::cout << std::hex << std::setfill('0') << std::setw(8) << x << std::dec << std::endl;

(from the C++ FQA). Functional programming is obsolete. In fact, functions are obsolete: at most, you can have a class with a single method. Prolixity is praiseworthy. Beautiful gems like this Haskell line to compute the Fibonacci numbers:

fib = (fibs !!) where fibs = 0 : scanl (+) 1 fibs

(from Wikipedia) are too hard to understand. Instead, students are taught:

public class Fibonacci {
  public static long fib(int n) {
    if (n <= 1) return n;
    else return fib(n-1) + fib(n-2);
  }
  public static void main(String[] args) {
    int N = Integer.parseInt(args[0]);
    for (int i = 1; i <= N; i++)
      System.out.println(i + ": " + fib(i));
  }
}

New programming languages must be object-oriented, or at least pretend to be, in order to be taken seriously. Even Guy Steele joined the dark side.

A return to normalcy

Not all was lost. Some saw through the ruse:

  • Rob Pike observed “object-oriented design is the roman numerals of computing” and C function pointers yield the same supposed benefits, except in a more flexible manner.
  • Edsger Dijkstra joked that “object-oriented programming is an exceptionally bad idea which could only have originated in California.”
  • Linus Torvalds stuck to plain C, which “means that you get a lot of programmers that do actually understand low-level issues and don’t screw things up with any idiotic "object model" crap.”

though their admonitions mostly fell on deaf ears. See the Wikipedia article on object-oriented programming concludes with numerous similar quotes. See Object Oriented Programming Oversold! for more. My favourite criticism from this comprehensive site is that proponents claim OOP better fits the way people think, while simultaneously claiming it takes years of study to develop proficiency. How can it take years of study to think naturally?

Other object-oriented programming preachers rebelled in practice, by spending most of their time in “fun” languages like Perl or Python. After all, who enjoys the boilerplate and verbosity of Java?

As the ideology aged, it lost its novelty. I bet that many who invested heavily to switch to objects failed to see the promised returns, and are now skeptical. Conditions are at last ripe for an escape from the Dark Ages. Perhaps it’s confirmation bias, but I see signs of Enlightenment approaching.

New languages like Erlang and Go have become popular despite not being object-oriented. Conversely, object-oriented languages are gaining features from functional programming. Already in the late 1990s, Java 1.1 supported anonymous inline classes, which makes functional programming almost bearable. Years later, Scala, with significant support for functional programming was built on top of Java infrastructure. C++ 2011 supports lambda functions.

Google’s MapReduce framework has been battle-tested and acquired fame. Its name and design was inspired by functional programming constructs, which may encourage programmers to learn about them.

The tide may be turning in academia. Robert Dewar, a former computer science professor at New York University laments the lower quality of degrees after they started emphasising Java. More recently, Carnegie Mellon University eliminated object-oriented programming from the introductory computer science curriculum, because it is intrinsically anti-modular and anti-parallel. See also "Why Functional Programming Matters".

My generation is lost, but there is hope for the future.

Class struggle

In one word, “inheritance” sums up the trouble with object-oriented programming. This cursed concept is responsible for everything from meaningless debates about is-a versus has-a, to real problems such as the inheritance anomaly.

I can understand why inheritance caught on as a brilliant way to reuse code. With a single declaration, we avoid duplicating a bunch of member and method definitions. However, it’s like lighting a furnace to burn a hair. Wholesale copying is blunt and crude, and is rarely desirable. How do we reuse code from more than one source? What if we only want to reuse certain bits? Will my changes to a class wreck a subclass?

Abstract art

My misadventure with object-oriented programming was not entirely fruitless. I learned about pure abstract base classes. Over time I saw how they dissolved away many problems I encountered with inheritance.

In pure abstract classes since we declare only the public methods, so we limit the damage that inheritance can do, as it cannot duplicate any implementation details. In this context, inheritance actually becomes useful.

So why not make pure abstract classes the only kind available? Java almost does: its interfaces are pure abstract classes. Unfortunately, every class is allowed to sin once: you get one free evil inheritance.

Other languages have succeeded, permitting one fewer inheritance per class than Java. For example, pure abstract classes resemble Go interfaces and Haskell type classes, and in these languages, there is no object-oriented-style inheritance.