This apparently goes back to part of the inspiration of Smalltalk (dataless programming). It enables you to create an ubiquitous language and radically improves your ability to structure a program in terms of reusable abstractions. It is fundamentally about hiding complexity and specificity on one end and only requiring or assuming what you _need_ to require or assume on the other.
In comparison to this, inheritance feels like a half baked implementation detail that got out of hand. Especially static inheritance hierarchies feel like the antithesis of object orientation. Pinning everything down and turning a codebase into immovable concrete.
- Alan Kay -
Other remarks from him about OOP are (paraphrased):
- OOP is a recursion on the notion of the computer itself
- The internet\Erlang is more OOP than java
His vision for OOP is something akin to distributed computing: Each object is a seperate computer with its own private memory that absolutely nobody can write to, it presents to the world an interface which is the set of all messages it can respond to (messages might or might not be asynchronous, and might or might not be unreliable in order,delivery,etc...). "Late Binding" because you have zero gaurantees about anything not explicitly stated in the object's contract, anything the object doesn't promise to respect will be violated, you better not depend on it. This is to give freedom to as much different implementation of the same interface as possible.
Two other analogies I encountered are
- Objects are like hardware ICs (their interfaces are the external pins and the type of signals the IC expects on them, their internals is the private hardware and signals that nobody is expected to inspect or modify)
- Objects are like seperate programs (their interfaces are the GUI/cmd the program presents and the set of all possible actions you can do, their internals are the executable code and in-memory data structures that nobody is expected to inspect or modify)
Inheritance actually precedes mature OOP, it was introduced way back in Simula-62, the proto-OOP language that wasn't a proper language as much as a simulation framework. Inheritance makes perfect sense in a simulation framework, because you're modelling a small world with a definite ontology that you can articulate and be sure of, the creators of Simula stumbled on it when they found they were repeatedly duplicating code verbatium. The analogies of Car<Vehicle and Cat<Animal make perfect sense, this is what inheritance is really made for, it's just that most real software isn't discrete event simulations with platonic ontologies.
If one squints hard enough, one could argue that we are continuing to move in his direction even so, slowly and in fits. But it's not because we have an Alay-Kay-OO language, and to the extent it is happening, it's not clear that it's happening at a level of abstraction that languages matter much with. When people use OO as a term today, it's a different definition.
Rust is a bit closer to the older OO ideal in that each object is its own private machine that encapsulates/protects its own state, and is late bound. The borrow checker allows the compiler to statically check that each use case of a given class is memory/threadsafe, and enforces basic state machines like "finish using this reference before calling next on the iterator", or "give up the ability to read() this network handle before transferring it to another thread". Modern OO completely fails to provide safety for those sorts of APIs (e.g. Java's ConcurrentModificationException is a compile time error in Rust)
On top of that base, you can build progressively more expressive OO-style API contracts, such as connection pools.
All programming paradigms can do that, you are not making any particularly salient point about OOP here.
OOP doesn't mean that everything needs to be an object, but inheritance and shared behaviors are concepts that map neatly between the real world and the programming world, which is why OOP has been such a resounding success.
We have refined this approach now (e.g. using delegation over inheritance), but these basic concepts of shared behavior and reuse remain.
"Modeling the world as OOP", OTOH, is a load of bull.
… we use different sets of building blocks for modelling the world and modelling the software …"
1994 "Designing Object Systems"
https://www.google.com/books/edition/Designing_Object_System...
Personally, after working with vanilla JS, then jquery, I looked at angular and react. I liked react so much better
I do agree with most of his points though, which just so happens to be closely aligned with how I've ended up programming. I like to use interfaces, so my objects hierarchies are very shallow and use a lot of delegation, overriding few if any methods.
I also write a lot of free-standing functions, some long, when I feel that's best. I also mix in a fair bit of functional-like programming, especially when massaging data.
I totally agree with you. I think he focuses on this style because it's generally what's taught in Universities, and he also has tons of tutorials geared towards students. So he's exposing his audience to the idea that some OOP principles are good, but overly adhering to the paradigm (in the way that would get you a 100% grade in an OOP class in college) is actually a bad way to program in general.
Being self-taught, I was already a proficient programmer in Delphi and C++ by the time I went to University. So to me OOP has always been "OOP when you need it".
Though now that you mention it, even by the time I started University I had a profound dislike for Java's "OO all the things" approach. It has not subsided...
1994 "It should be clear to anyone that models of the world are completely different from models of software. The world does not consist of objects sending each other messages, and we would have to be seriously mesmerised by object jargon to believe that it does.
… we use different sets of building blocks for modelling the world and modelling the software …"
page 6 "Designing Object Systems"
https://www.google.com/books/edition/Designing_Object_System...
Yea, sure, we didn't gain anything.
Except milions of programmers being able to model complex real world into their software that powers almost everything now.
Even if it is "shitty" "hard to maintain"
then it at least works
If I need some functionality, I write a function and pass it a struct. I’ve yet to find a time when this approach is too limiting, or chaotic.
Even for interfaces, function pointers achieve that goal entirely.
I do miss type safe vectors, e.g templating. Thats above and beyond the most common thing I miss.
There's a certain conceptual and implementation elegance to OoO so I understand why it became popular. But in practice all the new languages moving away from it are doing so for good reason.
`thing.doWork(arg)`
`doWork(thing, arg)`
`doThingWork(thing, arg)`
The first example is marginally more readable IMO. The second one sucks from a human perspective because we don't immediately know (unlike the IDE) that the function signature only accepts Things. The last one addresses this but is even longer, and we still have to mentally unpack the args.
IMO there is not much profound about OOP, but it can lead to slightly more readable code if used appropriately.
I can admit though that from an IDE point of view having auto-completion upon writing thing and typing the dot and seeing the results is very useful, whereas there is nothing (as far as I know) that comes even close in functional languages where the second example is more typical.
Functions can be invoked in at least 3 different ways or a mixture of the three
1. Prefix - C style f(x,y,z)
2. Infix - x + y; x.f(y,z); a join b
3. Post fix- (x,y,z) $ f
Infix is the best style in most cases, OOP accidentally helped programmers use a more readable infix style.
A lot of the benefits of OOP can be had by allowing good infix invocation support and interface support in an imperative non OOP language. Might as well throw in generics.
Infix is tricky because it doesn't scale well to complex expressions. I think Python does it right with plenty of infix keywords a la "if thing not in myset: ...".
Long function invocations are an annoying consequence.
I feel like classic OOP is a mistake because you're binding data objects with functional objects. The latter being a collection of functions that performs operations on the former.
If you break them up you can have multiple widgets for dealing with a data object. Each of them with whatever dependencies they need and nothing else. I feel like this style of programing has become more important because it fits in the the idea of computing as an assembly line. Passing data through a data pipelines. Steps which often happen on different processes or machines.
Dependency injection is also really really useful for clean C code.
At first I was enthusiastic. Then I've realized that there might be better ways to solve problems than trying to fit everything in a "everything is an object" mentality.
OOP can lead to overly complex code, uneeeded abstractions and design patterns thrown on top of each other for no good reason.
There was a thinking that using objects you can model or imitate real life, but that is silly. And of course, programming doesn't deal with real life, but with bytes and instructions, data and actions performed on the data.
I still use OOP because most of the industry demands it, but when I can, I try to use a data oriented and a bit of a functional approach, even if I am using an OOP language.