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.
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.