> his combination of data and code in a strict hierarchy means that all data access patterns are baked into this dependency tree
I believe OOP is really good at modelling state and really bad at modelling data.
With state (and side-effects modeled by state machines) you want a well-defined interface, messages, local retention and so on. This way you get the benefit of reasoning about state in a constrained manner both as the caller and the callee.
For example it makes sense to model Mario as a state-machine that receives messages from game-pad (pressed buttons) and collision events. Note that this is a conceptual, high level kind of modelling and not a concrete, structural one.
But data is about something. There is data about Mario at any given point in time: his physical dimensions, his velocity, consumed power ups and so on.
This is where OOP makes no sense at all. Reading and transforming data should be universal/uniform rather than guarded by idiosyncratic getters, setters and so on.
This is why SQL, JSON, XML, HTML, Unix filters, AWK, Functional Programming etc. are so powerful. These technologies provide/enable a uniform/universal way of reading data and composing transformations.
(As a side-note: I consider state that never leaks as an implementation detail, usually for performance)
> his physical dimensions, his velocity, consumed power ups and so on.
Aren't his physical dimensions just attributes of the Mario instance (or class definition)?
Similarly, his velocity, consumed power ups and so on can all be attributes of the stage he's in. His extra lives can be an attribute of this current game session.
Usually that just shows the lack of understanding of how to use OOP, and what should be proper classes, what should be interfaces or categories, depending on the language.
Thats the point. There shouldn’t be any deeper understanding necessary at all to represent the simplest and most prevalent case conveniently and uniformly.
Many of the core principles of OO make sense in a stateful context, that doesn’t automatically make them a good fit for data as well.
Same argument can be done for FP or ADT (abstract data types).
Some people like to seat at the keyboard and code away, but don't expect stellar results without taking the effort to learn best practices, by reading books and such.
Well, quite, but that doesn't obviate the point. What you're describing has an exact corollary: "Saying FP is not as good at handling state as OOP just shows a lack of understanding of FP, of structures such as the state monad."
Yes, using strict patterns to deal with the weak points of the overall paradigm is absolutely fine, but it's patching over cracks rather than using the best tool for the job. OOP is very good at handling state but less good at pipelining data, FP is very good at pipelining data but less good at handling state.
Languages being multi paradigm reflects that people want to use the best tool for the job, not lack of learning. This stems from favoring pragmatism over style.
The conclusions about OOP and FP having their specific trade-offs are not made by people who don't learn, but by people who did and then applied different methods and see their trade-offs.
But neither of us are talking talking about cargo culting FP or OO, I think you have a bee in your bonnet about something that isn't really related to what we're saying. All we're saying is that there are pragmatic reasons for using an FP approach or an OO approach depending on situation, that it isn't due to a lack of understanding.
Right, the lack of understanding comes from bashing OOP from FP angle, without realizing that all FP languages that left academia and are actually used across the industry support the same principles in one way or the other.
Statements like "I hate OOP, hence I do Clojure/Lisp", which I just hand over a copy of the "Art of Metaobject Protocol".
Wirth's Algorithms + Data Structures = Programs ideas seem to have fallen by the wayside.
It should be about learning concepts, not about feature X in language Y.
But neither of us were saying that, that's my point. I totally agree with you! But it's not bashing OOP from an FP angle or vice versa, it's that those paradigms (patterns I guess is a better descriptor, but that term has been co-opted a bit by OOP) each have a place. And within a multi-paradigm language both can be used together, and yes, dogmatic avoidance of one or the other is fairly widespread and a bit silly (and particularly silly when both are immediately available).
Which is a bizarre stance. Rich Hickey has always said that Clojure has great OOP support, it just doesn't force an all or nothing approach on you. Do you want inheritance without classes? You can have that. Want interface? Dispatch? Encapsulation? Whatever OOP feature you want, Clojure likely has support for it (without dropping to Java, obviously you can use Java OOP if you really want, too), it just lets you mix and match the individual features and apply them as makes sense to you. And Common Lisp has CLOS.
So, yeah, I agree that the common "OOP bad, FP good" sentiment is missing the point.
With that said, I personally do believe that OOP is a bad default. By which I mean, in any typical non-trivial program I write, the amount of OOP that I feel makes sense is usually not the dominant part (but its still a significant part), that usually I don't want to hide data at all preferring data structures that can be accessed directly (like in Python or Clojure) and that most data processing is funcitonal-like transformations. I also feel that immutable state is the best default as mutable state should be carefully thought about and managed. Basically, in my opinion, OOP should be de-emphasized somewhat, but its still an important tool that I use in pretty much every non-trivial project.
For example, I've recently written a successful cryptocurrency trading bot where the trading logic is a pure function of market data, state of your orders and your strategies local state, running in a database transaction, and its output is a sequence of actions to be performed (orders to place/cancel/edit, new local state to persist to database, etc). It uses an OOP style architecture for its top level services and to abstract the different data sources and exchanges, but the actual decision making logic is a pure functional sequence of transformations (under the hood; what the user actually sees is an event driven state machine that generates the desired world state, which gets diffed with the actual world state to produce the required actions). Basically, an FP core wrapped in an OOP set of DI-able services. Its worked out very well.
Right, so if they're multi paradigm, then why would you not use FP features for the parts of a given application that play to FP strengths, and OO features for the parts that play to OO strengths? Why try to squash the former into the latter when you could just use the former directly?
I'm just struggling to see your point here. You're saying it demonstrates a lack of effort in learning best practices whilst also saying that it's a lack of understanding of how to use OO. If it's a multi paradim language and you ignore FP approaches that are fully supported in favour of OO patterns (to deal with the same thing!) I'm not quite sure that's a lack of effort learning best practices. It's more "I have an OO hammer, I'll hammer away with that"
I believe OOP is really good at modelling state and really bad at modelling data.
With state (and side-effects modeled by state machines) you want a well-defined interface, messages, local retention and so on. This way you get the benefit of reasoning about state in a constrained manner both as the caller and the callee.
For example it makes sense to model Mario as a state-machine that receives messages from game-pad (pressed buttons) and collision events. Note that this is a conceptual, high level kind of modelling and not a concrete, structural one.
But data is about something. There is data about Mario at any given point in time: his physical dimensions, his velocity, consumed power ups and so on.
This is where OOP makes no sense at all. Reading and transforming data should be universal/uniform rather than guarded by idiosyncratic getters, setters and so on.
This is why SQL, JSON, XML, HTML, Unix filters, AWK, Functional Programming etc. are so powerful. These technologies provide/enable a uniform/universal way of reading data and composing transformations.
(As a side-note: I consider state that never leaks as an implementation detail, usually for performance)