4 ms·
> The main problem with OOP is the promotion of mute-able state When the mutable state is essential (like in my example for a file stream object) that’s not a
by Const-me 6y ago
> The main problem with OOP is the promotion of mute-able state
When the mutable state is essential (like in my example for a file stream object) that’s not a problem at all, quite the opposite, it allows to expose idiomatic API from a library/module, while hiding implementation details from consumers.
> Though semantically equivalent object_verb(object) is actually better than object.verb
All modern IDEs have auto-complete, you type x. in any statically types OO language and you’ll get a list of all public methods of the class. object_verb(object) can’t do that kind of contextual stuff.
> All three options are bad for various reasons
They’re also all good for other reasons. This applies to all options whatsoever, not just to these 3. Software engineering is all about the tradeoffs, which tradeoff is the best depends on requirements, language, platform, team, budget, users, and many other factors.
1 is good when the majority of the implementation is shared, and A/B only need to override a couple of methods. Many OO languages have private/protected/public visibility to be able to selectively hide implementation details even from derived classes. An example is a video encoder or decoder: it has 90% of code shared across all codecs, and only the remaining 10% depends on whether the codec is h264 or h265.
2 is good when the code is simple like your example. Also when you expect you might want to change the method in one of the types but not the other despite they’re equivalent right now. Also that version is the most readable of them.
3 can be good when you expect you might want to change the implementation of the object being injected while keeping the interface of that object, albeit I don’t normally write stuff like that unless I have very good reasons.
Your method is bad if the code is performance critical, your call stack is twice deeper than 1 or 2. Even ignoring performance, deep stacks complicate debugging, all debuggers show call stacks, they may also be visible in other places like crash dumps, exception traces, or logs.
> If I want to deal with mutable state, it's actually not technically FP anymore
I don’t particularly want to, but for 99% of real-world software dealing with mutable state is essential part of the problem.
A lot of software is I/O bound and/or the I/O is the main source of their complexity. Database engines spend majority of time doing disk and network I/O. For servers and web apps that’s network I/O. Videogames mutate external state in VRAM at a rate of gigabytes/second. Every device connected to the Internets runs many thousands of lines of code layering protocols on top of each other, a session on every layer is a large piece of mutable state (buffering everywhere, decompressor state for GZIP, decryption stream state for TLS, and so on).
GUI apps have a huge chunk of mutable state. For an editor like Word it’s obvious why, user can edit anything however they please. But that’s true even for readonly or mostly read-only GUI, e.g. most web pages are read only yet the browsers implement mutable DOM trees, the second letter of the acronym is for “Object”.
- leafboi 6y ago>When the mutable state is essential (like in my example for a file stream object) that’s not a problem at all, quite the opposite, it allows to expose idiomatic API from a library/module, while hiding implementation details from consumers. Yes, When mutable state is essential pure FP cannot help you. However much of FP styled languages / frameworks are designed to segregate mutable state away from your awareness as much as possible. See React or Haskell. Even so, like I illustrated in the Jungle banana gorilla example. When you have mutable state, it is better to deal with it without using OOP. Take a look at that example again, see how the procedural function is better than the OOP method. >Your method is bad if the code is performance critical, your call stack is twice deeper than 1 or 2. Even ignoring performance, deep stacks complicate debugging, all debuggers show call stacks, they may also be visible in other places like crash dumps, exception traces, or logs We can talk about the qualitative benefits of each but I'm sure you'll agree from a language/API perspective... My method is the best method in terms of reusability. If the focus of your code is reusability and modularity FP is the best way to go. However you are right that there is inevitably a performance cost. Computers are essentially machines that rely on mutability and have memory boundaries. Any functional abstraction implementation on top of such a machine will not be a zero cost abstraction. We're in agreement here. However you will note that for most modern programming the performance cost is no longer a factor that matters. Either computers are way too fast and make the performance cost negligible or there are bottlenecks in other parts of the system that make the performance gain negligible. Games and Databases are two examples where FP may not be the most appropriate paradigm due to the performance critical nature of the areas. >I don’t particularly want to, but for 99% of real-world software dealing with mutable state is essential part of the problem. Yeah A lot of FP languages have abstractions that help you get around this. See Haskell or React, look up FRP and the IO Monad. Not trivial research subjects. Look think about it this way. OOP promotes shared state and mutable state in places where it isn't necessary. FP forces you to use a minimum amount of shared as possible and segregates that away from your program. There are essentially two types of functions: functionThatGetsDataFromIOOrChangesState() functionThatTakesInputANdHasAnOutput() FP has abstractions that highly segregates these two types of functions. OOP has facilities that integrates all these functions together into arbitrary groups called objects. The purpose of a class is to hold mutable state and integrate side effects with logic. If your class does not hold mutable state then essentially it's just a class repurposed as a namespace with syntactic sugar around the object_verb(object) semantics. If you are writing a low level program that deals with a lot of mutable state or IO, FP can still operate in this arena via the abstractions but if performance is super critical then probably don't use FP for this stuff. Still, better than OOP for mutable stuff is just a straight up procedural paradigm as I showed you in the banana jungle gorilla example. IO and mutable state needs to be separate from Logic in order to promote reusability. This is the key area where OOP fails. Procedural programming can achieve this separation while Functional Programming Forces you to achieve this separation. FP has its downsides and upsides, it is however the most reusable and modular paradigm that currently exists. If modularity and elegance is your objective nothing I know of beats it. As for OOP, when you bring in procedural programming into the picture, OOP no longer has any upsides. Again, see the banana example.