> These conversations aren't helped by the fact that OOP isn't well-defined, and observations about 'typical' OOP--the banana that has a reference to the gorilla and transitively the entire jungle, or the pervasive abuse of inheritance--are "aren't really OOP and you can write bad code in any paradigm!"
From Alan Kay (who coined the term)[1]:
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.
Almost anyone who has dealt with present-day OOP (Python, Ruby, C++, C#, others, or, God forbid, Java) has had the same thoughts as you, I'm guessing. I've come to the conclusion that OO as originally envisioned had a lot of good ideas, and these are surprisingly compatible with and complementary to a lot of the (almost-)pure FP ideas that have gained traction over the last few years.
OOP also had huge mistakes:
- Class inheritance is the biggest one. Kay himself mentions inheritance appearing only in the later versions of Xerox's Smalltalk. No need to really elaborate. Implement interfaces, compose behavior and don't inherit. He has posted some more of his thoughts on this exact topic. [2]
- Java was a giant industry-harming one. Now multiple generations of programmers have been brought up to think that the definition of an object is to write a class (which is not an essential characteristic), declare internal state and then write accessors and mutators for every one of them. We might as well write COBOL. But, having tried to solve problems in Java, I get why it's done. The OO is so weak and awful that I'd rather just crank out slightly-safer C in Java than jump through the horrific hoops. (Just as an aside, I find it sad that Sun had the teams that developed both Java and Self. Self used prototypes instead of classes, which influenced Javascript, but also had a killer JIT that made it super fast and a ground-breaking generational garbage collector. Sun ended up cannibalizing the Self team to assist with Java, and some of the features of the JVM were ports of work that had originally been done for the Self VM.)
- C++ was kind of a mistake, because it introduced the interminable "public" and "private" and now the endless litany of stupid access modifiers. They were needed because C++ had this need to be compatible with C and do dynamic dispatch and have some semblance of type safety (though C is only weakly typed) and have it run (and compile) before the heat death of the universe. C++ isn't as horrible as what came after it though IMO (i.e. Java).
- Xerox horribly mismanaging Smalltalk was another mistake. Xerox was smart enough to realize that Smalltalk was insanely valuable, but instead of realizing that it should be a loss leader/free drugs, they decided to lock it away in the high tower and then let everyone else pillage their research that could be commercialized (GUIs on the desktop, laser printers, Ethernet, among many others). This literally led Apple to develop Objective-C.
I really went down a rabbit hole on this topic around two years ago. Two ideas really helped crystalize the idea for me, both from Kay.
One is that the Internet is an object-oriented system. It communicates via message-passing, it is fault-tolerant, and all of its behavior is defined at runtime (i.e. dynamically bound). We don't need to bring the entire Internet down when someone develops a new network protocol or when a gateway dies or some other problem happens. It continues to work and has in fact never gone down (though early on during ARPANet, there were instances of synchronous upgrades [3]).
The other, deeper one (and the much more open one) is that "data" is error-prone and should be avoided and is, in fact, "the problem" (i.e. the root of the Software Crisis). This sounds preposterous to most programmers.[4] In fact, I didn't even get it when I first heard it. I think the basic idea is that he uses "data" as a physicist or statistician would use it: quantities measured in the world with varying accuracy/precision. This seemed preposterous or even heretical until I started noticing it in the large codebase I was working on at the time. A lot of the code was in a nominally object-oriented language, but the style was very procedural (i.e. "If a then do X, do Y; else do Z"). I noticed that all of our code was working on large data structures (think a relational database schema), but there was no coherent description of it or what it meant anywhere. If I touched one of the pieces of code, I invariably had to start adding more conditional branches in order to preserve correctness. In order to make any sense of the code, I made a ton of implicit assumptions about what the patterns I saw actually meant, rather than relying on the program itself to communicate this through its code or meaningful constraints on the data. We would be better served with "information" with less noise and more signal than what we get with data and data structures (think Shannon). The only real success we've had with data is a few encoding schemes (e.g. ASCII/Unicode, 8-bit bytes, IEEE-754, etc.), but that approach clearly doesn't scale beyond a dozen or two data encoding schemes. OO (of the high signal-to-noise variety championed by Kay) at least has a story for this, which is that certain patterns do indeed have predictable characteristics, and complex patterns can usually be composed by a handful of simple ones. I have yet to see a system like this actually in practice, but I can imagine something like it could exist, given the appropriate tools (in particular, a good, state-of-the-art language that isn't some rehash of stuff from the 60s - Erlang might fit the bill).
The ideas of DO as articulated by Mike Acton are antithetical to the ideas of OO (although a well-designed future OO system - including late bound hardware - could probably represent and implement a lot of the techniques that Acton talks about to improve performance and quality). This doesn't mean they're wrong, but I see treating data as the _only_ thing that matters as being fundamentally opposed to the idea that data is actually the root of most evil. People adopting DO should be aware that robustly applying the technique in the long run will require having detailed knowledge of every aspect of the system on every change. (I think it's not a coincidence that it was originally developed for game dev, which often doesn't have the same long-term maintenance requirements as other kinds of software, and certainly not software like the Internet.)
FP and OO are more complementary. Immutability and referential transparency can be very valuable in object systems; encapsulation, extreme late binding and message passing can be very valuable in functional systems (in fact, Haskell and Erlang surpass "OO" languages when scored on some of these characteristics). Scala explicitly embraces the complementarity between the two paradigms.
Oof, I just looked at the formatting of this post and realized I rendered the list wrong. Here's the non-eyebleed version:
- Class inheritance is the biggest one. Kay himself mentions inheritance appearing only in the later versions of Xerox's Smalltalk. No need to really elaborate. Implement interfaces, compose behavior and don't inherit. He has posted some more of his thoughts on this exact topic.[2]
- Java was a giant industry-harming one. Now multiple generations of programmers have been brought up to think that the definition of an object is to write a class (which is not an essential characteristic), declare internal state and then write accessors and mutators for every one of them. We might as well write COBOL. But, having tried to solve problems in Java, I get why it's done. The OO is so weak and awful that I'd rather just crank out slightly-safer C in Java than jump through the horrific hoops. (Just as an aside, I find it sad that Sun had the teams that developed both Java and Self. Self used prototypes instead of classes, which influenced Javascript, but also had a killer JIT that made it super fast and a ground-breaking generational garbage collector. Sun ended up cannibalizing the Self team to assist with Java, and some of the features of the JVM were ports of work that had originally been done for the Self VM.)
- C++ was kind of a mistake, because it introduced the interminable "public" and "private" and now the endless litany of stupid access modifiers. They were needed because C++ had this need to be compatible with C and do dynamic dispatch and have some semblance of type safety (though C is only weakly typed) and have it run (and compile) before the heat death of the universe. C++ isn't as horrible as what came after it though IMO (i.e. Java).
- Xerox horribly mismanaging Smalltalk was another mistake. Xerox was smart enough to realize that Smalltalk was insanely valuable, but instead of realizing that it should be a loss leader/free drugs, they decided to lock it away in the high tower and then let everyone else pillage their research that could be commercialized (GUIs on the desktop, laser printers, Ethernet, among many others). This literally led Apple to develop Objective-C.
From Alan Kay (who coined the term)[1]:
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.
Almost anyone who has dealt with present-day OOP (Python, Ruby, C++, C#, others, or, God forbid, Java) has had the same thoughts as you, I'm guessing. I've come to the conclusion that OO as originally envisioned had a lot of good ideas, and these are surprisingly compatible with and complementary to a lot of the (almost-)pure FP ideas that have gained traction over the last few years.
OOP also had huge mistakes:
- Class inheritance is the biggest one. Kay himself mentions inheritance appearing only in the later versions of Xerox's Smalltalk. No need to really elaborate. Implement interfaces, compose behavior and don't inherit. He has posted some more of his thoughts on this exact topic. [2] - Java was a giant industry-harming one. Now multiple generations of programmers have been brought up to think that the definition of an object is to write a class (which is not an essential characteristic), declare internal state and then write accessors and mutators for every one of them. We might as well write COBOL. But, having tried to solve problems in Java, I get why it's done. The OO is so weak and awful that I'd rather just crank out slightly-safer C in Java than jump through the horrific hoops. (Just as an aside, I find it sad that Sun had the teams that developed both Java and Self. Self used prototypes instead of classes, which influenced Javascript, but also had a killer JIT that made it super fast and a ground-breaking generational garbage collector. Sun ended up cannibalizing the Self team to assist with Java, and some of the features of the JVM were ports of work that had originally been done for the Self VM.) - C++ was kind of a mistake, because it introduced the interminable "public" and "private" and now the endless litany of stupid access modifiers. They were needed because C++ had this need to be compatible with C and do dynamic dispatch and have some semblance of type safety (though C is only weakly typed) and have it run (and compile) before the heat death of the universe. C++ isn't as horrible as what came after it though IMO (i.e. Java). - Xerox horribly mismanaging Smalltalk was another mistake. Xerox was smart enough to realize that Smalltalk was insanely valuable, but instead of realizing that it should be a loss leader/free drugs, they decided to lock it away in the high tower and then let everyone else pillage their research that could be commercialized (GUIs on the desktop, laser printers, Ethernet, among many others). This literally led Apple to develop Objective-C.
I really went down a rabbit hole on this topic around two years ago. Two ideas really helped crystalize the idea for me, both from Kay.
One is that the Internet is an object-oriented system. It communicates via message-passing, it is fault-tolerant, and all of its behavior is defined at runtime (i.e. dynamically bound). We don't need to bring the entire Internet down when someone develops a new network protocol or when a gateway dies or some other problem happens. It continues to work and has in fact never gone down (though early on during ARPANet, there were instances of synchronous upgrades [3]).
The other, deeper one (and the much more open one) is that "data" is error-prone and should be avoided and is, in fact, "the problem" (i.e. the root of the Software Crisis). This sounds preposterous to most programmers.[4] In fact, I didn't even get it when I first heard it. I think the basic idea is that he uses "data" as a physicist or statistician would use it: quantities measured in the world with varying accuracy/precision. This seemed preposterous or even heretical until I started noticing it in the large codebase I was working on at the time. A lot of the code was in a nominally object-oriented language, but the style was very procedural (i.e. "If a then do X, do Y; else do Z"). I noticed that all of our code was working on large data structures (think a relational database schema), but there was no coherent description of it or what it meant anywhere. If I touched one of the pieces of code, I invariably had to start adding more conditional branches in order to preserve correctness. In order to make any sense of the code, I made a ton of implicit assumptions about what the patterns I saw actually meant, rather than relying on the program itself to communicate this through its code or meaningful constraints on the data. We would be better served with "information" with less noise and more signal than what we get with data and data structures (think Shannon). The only real success we've had with data is a few encoding schemes (e.g. ASCII/Unicode, 8-bit bytes, IEEE-754, etc.), but that approach clearly doesn't scale beyond a dozen or two data encoding schemes. OO (of the high signal-to-noise variety championed by Kay) at least has a story for this, which is that certain patterns do indeed have predictable characteristics, and complex patterns can usually be composed by a handful of simple ones. I have yet to see a system like this actually in practice, but I can imagine something like it could exist, given the appropriate tools (in particular, a good, state-of-the-art language that isn't some rehash of stuff from the 60s - Erlang might fit the bill).
The ideas of DO as articulated by Mike Acton are antithetical to the ideas of OO (although a well-designed future OO system - including late bound hardware - could probably represent and implement a lot of the techniques that Acton talks about to improve performance and quality). This doesn't mean they're wrong, but I see treating data as the _only_ thing that matters as being fundamentally opposed to the idea that data is actually the root of most evil. People adopting DO should be aware that robustly applying the technique in the long run will require having detailed knowledge of every aspect of the system on every change. (I think it's not a coincidence that it was originally developed for game dev, which often doesn't have the same long-term maintenance requirements as other kinds of software, and certainly not software like the Internet.)
FP and OO are more complementary. Immutability and referential transparency can be very valuable in object systems; encapsulation, extreme late binding and message passing can be very valuable in functional systems (in fact, Haskell and Erlang surpass "OO" languages when scored on some of these characteristics). Scala explicitly embraces the complementarity between the two paradigms.
[1] https://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_ka...
[2] https://www.quora.com/What-does-Alan-Kay-think-about-inherit...
[3] https://en.wikipedia.org/wiki/Flag_day_(computing)
[4] https://news.ycombinator.com/item?id=11945869