March 12th, 2015
(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: firstname.lastname@example.org
A lot of people have read my essay from October 7th, 2014, “Object Oriented Programming is an expensive disaster which must end“. Many people like the essay, and many people hate the essay. The people who hate the essay often raise the possibility that I am a troll. They also feel that the whole essay should be dismissed based on its mistakes.
I have been meaning to write a second version that fixes all the mistakes of the first. The initial post was written as a blog post, and so I did not fact-check it the way I might have if it were a book. If I had known how popular the post would be, then I would have been more rigorous. The essay is very long, 30,000 words, so it will take me some time to write a second version.
Scala programmers, in particular, have hated this essay because I used a bad example of Scala in the essay. I do not know Scala very well, so I probably should have left that example out. However, that one example is such a tiny part of the essay, that if anyone is willing to dismiss the essay because of that one example, I think it is reasonable to say that they were looking for any possible reason to dismiss the essay.
So the author seems to be unaware that OOP can be done with immutable objects. Does that discredit the entire article? I think so, but apparently not everyone agrees. In fact, there seems to be a lot of outright hostility to OOP out there. The article claims that everything that can be done with OOP can be done in a simpler and better way with pure FP. I’ve seen this claim before.
Is it OOP if you only use immutable objects? As I said in the essay, you can use Java and write in the Functional Style — but if you are writing in the Functional Style, you might want to use a language other than Java, since Java works against you when you try to be Functional. You can use almost any language to achieve the Functional Style, but if you are trying to achieve the Functional Style, why not use a language that was designed, from the start, to support the Functional Style?
Scala programmers are proud of the fact that Scala is multi-paradigm. However, if anyone criticizes the OOP features of Scala, the Scala programmers tend to react as if Scala is fundamentally OOP, such that any attack on OOP is also an attack on Scala. What Russ Paielli has written is a good example of this.
Bardur Arantsson followed up in that same thread by writing:
I think in this case the phrase “don’t feed the troll” may apply.
I agree with Arantsson that we should never waste time arguing with someone who is arguing in bad faith. However, I don’t think anyone can read my essay and sincerely believe that I was arguing in bad faith. And if you are talking to someone who is an advancing an argument, and offering supporting evidence, and who makes their argument in good faith, and then you accuse them of being a troll, then that marks you as someone who is frightened of new ideas. Arantsson’s remark is defensive — he sounds like someone who has put his fingers in his ears, because he does not like the argument that he is hearing.
Those of us who have argued against object oriented programming run into the problem that the people defending it feel free to redefine it whenever someone makes a good argument against its worst flaws. Jonathan Rees does an excellent job of summarizing the frustration that we feel:
Because OOP is a moving target, OOP zealots will choose some subset of this menu by whim and then use it to try to convince you that you are a loser.
That is the short version. He wrote that back in 2001, and the situation has only gotten worse. The longer version:
I have heard OO defined to be many different subsets of this list.
Encapsulation – the ability to syntactically hide the implementation of a type. E.g. in C or Pascal you always know whether something is a struct or an array, but in CLU and Java you can hide the difference.
Protection – the inability of the client of a type to detect its implementation. This guarantees that a behavior-preserving change to an implementation will not break its clients, and also makes sure that things like passwords don’t leak out.
Ad hoc polymorphism – functions and data structures with parameters that can take on values of many different types.
Parametric polymorphism – functions and data structures that parameterize over arbitrary values (e.g. list of anything). ML and Lisp both have this. Java doesn’t quite because of its non-Object types.
Everything is an object – all values are objects. True in Smalltalk (?) but not in Java (because of int and friends).
All you can do is send a message (AYCDISAM) = Actors model – there is no direct manipulation of objects, only communication with (or invocation of) them. The presence of fields in Java violates this.
Specification inheritance = subtyping – there are distinct types known to the language with the property that a value of one type is as good as a value of another for the purposes of type correctness. (E.g. Java interface inheritance.)
Implementation inheritance/reuse – having written one pile of code, a similar pile (e.g. a superset) can be generated in a controlled manner, i.e. the code doesn’t have to be copied and edited. A limited and peculiar kind of abstraction. (E.g. Java class inheritance.)
Sum-of-product-of-function pattern – objects are (in effect) restricted to be functions that take as first argument a distinguished method key argument that is drawn from a finite set of simple names.
The defenders of OOP freely re-define the term so that it never means whatever you criticize. Russ Paielli’s remark is an example of that — in his view, OOP is really all about the Functional Style, where everything is immutable. I have to wonder why people have such a deeply emotional loyalty to OOP. After all, since Russ Paielli clearly admits that defaulting to immutability is a wise choice, why won’t he switch his allegiance to those languages that were invented to support that style? Perhaps Jonathan Rees gets at the emotions behind this issue when he writes:
In a pack you want to restrict everyone else’s freedom as much as possible to reduce their ability to interfere with and take advantage of you, and the only way to do that is by either becoming chief (dangerous and unlikely) or by submitting to the same rules that they do. If you submit to rules, you then want the rules to be liberal so that you have a chance of doing most of what you want to do, but not so liberal that others nail you.
In such a pack-programming world, the language is a constitution or set of by-laws, and the interpreter/compiler/QA-team acts in part as a rule checker/enforcer/police force. Co-programmers want to know: If I work with your code, will this help me or hurt me? Correctness is undecidable (and generally unenforceable), so managers go with whatever rule set (static type system, language restrictions, “lint” program, etc.) shows up at the door when the project starts.
There is a kind of mania for object-oriented programming at the moment, but some of the smartest programmers I know are some of the least excited about it.
My own feeling is that object-oriented programming is a useful technique in some cases, but it isn’t something that has to pervade every program you write. You should be able to define new types, but you shouldn’t have to express every program as the definition of new types.
Since 2005, there has been a reaction away from Object Oriented Programming, and Scala is a reflection of that reaction. Whereas, in the late 90s, languages were competing to be known as the most “pure” of all the Object Oriented Programming languages, nowadays the newer languages compete either by calling themselves multi-paradigm (Scala) or by emphasizing their “pure” commitment to non Object Oriented styles (F#, Clojure, etc). No one, in 2015, is trying to promote their favorite language by insisting that it is purest Object Oriented language out there. As I wrote in my essay:
The hype in favor of OOP built up during the 1980s and early 90s, but I would say the zenith of the idea was from 1995 to 2005. After 2005, very slowly, a reaction against OOP developed. This reaction initially showed 3 tendencies:
1.) a movement away from static typing and toward dynamic typing
2.) a movement away from compiled languages and toward script languages
3.) a movement away from heavy-weight frameworks and toward lighter frameworks
And then eventually a 4th tendency appeared:
4.) a movement away from OOP and toward the “functional” paradigm
In response to Russ Paielli, Martin Odersky joins the thread and writes:
Objects done well provide a lightweight module system (in the sense of ML). The requirement for a good module system is something that’s often overlooked when all you do is a simple application, but its lack becomes painfully apparent for large systems and systems that are maintained over time.
You can go the SML route and provide a module system and a functional core language. But then there’s the pressure that you want to generalize your modules to be more flexible, which means you want them to become recursive and first-class. If you do that, you end up with a de-facto object system in addition to your core language of algebraic data types and functions.
Scala’s approach of unifying modules and objects is more parsimonious: The same constructs define the core language and the module language. So Scala is simpler, in the sense that fewer concepts are needed to describe the total feature set.
1.) I am confused by “something that’s often overlooked” — who overlooks this? Who doubts that some kind module and/or namespace system is needed in large projects? This need tends to get heavy emphasis in every book and essay I have read on software architecture.
2.) “a lightweight module system” sounds good, though there are some known limits to the ML module system:
The ML module system stands as a high-water mark of programming language support for data abstraction. Nevertheless, it is not in a fully evolved state. One prominent weakness is that module interdependencies in ML are restricted to be acyclic, which means that mutually recursive functions and data types must be written in the same module even if they belong conceptually in different modules. Existing efforts to remedy this limitation either involve drastic changes to the notion of what a module is, or fail to allow mutually recursive modules to hide type information from one another.
3.) The phrase “Objects done well” is a frustrating example of what I was complaining about when I wrote:
The No True Scotsman fallacy leads to arguments like this:
Person A: “No Scotsman ever steals.”
Person B: “I know of a Scotsman who stole.”
Person A: “No True Scotsman would ever steal.”
Person A believes all Scotsman are brave and honorable, and you can not convince them otherwise, for any counter-example you bring up is of some degraded Untrue Scotsman, which has no bearing on whatever they think of True Scotsman. And this is my experience whenever I argue against Object Oriented Programming (OOP): no matter what evidence I bring up for consideration, it is dismissed as irrelevant. If I complain that Java is verbose, I’m told that True OOP Programmers let the IDE take care of some of the boilerplate, or perhaps I am told that Scala is better. If I complain that Scala involves too much ceremony, I’m told that Ruby lacks ceremony. If I complain about the dangers of monkey-patching in Ruby, I’m told that True OOP Programmers know how to use the meta-programming to their advantage, and if I can’t do it then I am simply incompetent. I should use a language that is more pure, or a language that is more practical, I should use a language that has compile-time static data-type checking, or I should use a language that gives me the freedom of dynamic typing.
Discussions of languages differences would be greatly improved if we could all give up phrases such as “done well” and focus on the overall tendencies that a language promotes.
4.) “But then there’s the pressure that you want to generalize your modules to be more flexible, which means you want them to become recursive and first-class” — but where is that pressure coming from, and are objects the answer? Erlang, in particular, seems to offer the alternative: further separation of the modules to the point that they are basically separate apps, each running in their own process, and each sending and receiving those messages that are allowed by their interface.
I am not sure what Odersky is attempting to say here, but it seems like he’s either stretching the definition of objects so wide that we can now consider Erlang to be an Object Oriented language (!!!) or he is saying that all languages secretly want to be Object Oriented (“you end up with a de-facto object system”) and they introduce unneeded complexity by trying to avoid objects (“Scala is simpler”). But if we re-define “module” and “object” such that any language with a module system is an Object Oriented language then the phrase “Object Oriented” becomes meaningless, because we would now be including several languages that are famous for rejecting Object Oriented Programming. But if he’s saying that any language that has modules faces pressures to make the modules more flexible because “you want them to become recursive and first-class”, then the “you” in the sentence is actually Martin Odersky, and he is merely re-stating his personal preferences. That is, if you give Martin Odersky a module, he prefers to turn it into an object. And I already knew that. But he is not saying why he prefers to turn modules into objects, he is simply re-stating his preference. Joe Armstrong (inventor of Erlang) had different preferences, and went in the completely opposite direction. And Joe Armstrong has written a fantastic work describing his reasons for doing so.
Also, this is odd:
“Scala is simpler, in the sense that fewer concepts are needed to describe the total feature set.”
Scala has always been proudly multi-paradigm, so surely “fewer concepts” has never been the goal? I am not saying that “fewer concepts” should be anyone’s goal, but if it is your goal, then Scala is a terrible choice for you. Pick a more specialized language for your task.
A final point about Russ Paielli words. He wrote: “OOP can be done with immutable objects.” If we were discussing soccer, I could imagine saying “My team can do defense”, and I might even get emotional when defending my team, but I didn’t think we were discussing teams here. We are discussing concepts, and we should try to keep track of ideas that are mutually exclusive. I would think computer programmers would be good at this.
Here are 3 statements that I think clarify the argument from my first essay:
1.) If most of your objects are mutable, but you have the occasional immutable object, then my original statement is accurate simply as it is written.
2.) If most of your objects are immutable, then you are writing in the Functional Style, and your Object Oriented Language is burdening you with a bunch of unnecessary complexity and ceremony — there are easier ways to achieve the Functional Style, in other languages.
3.) If Scala is multi-paradigm, then it is not purely Object Oriented. Therefore, if you need to do X, you can not write a piece of Scala code that does X and then claim “This proves that an Object Oriented language can do X”. Rather, you need to first identify which paradigm is in use in that piece of Scala code. You have then proven that the particular paradigm in use can do X.
Is this really so difficult to understand?Source