4 ms·
>Conceptually, procedural languages like C are doing that too for files, they’re just writing object.Verb as object_verb(Handle instance). The main problem wit
by leafboi 6y ago
>Conceptually, procedural languages like C are doing that too for files, they’re just writing object.Verb as object_verb(Handle instance).
The main problem with OOP is the promotion of mute-able state, this is what ties everything together and makes everything less modular. Though semantically equivalent object_verb(object) is actually better than object.verb when you consider mutations and side effects.
Seriously I want you to consider this: Two classes A and B.
class A:
def __init__(self, x):
self.x = x
heavy_computation()
def addOne(self):
self.x += 1
class B:
def __init__(self, x):
self.x = x
def addTwo(self):
self.x += 2
Let's say I want to write an addOne method for B. How do I reuse the code for addOne in A inside of B?
1. Inheritance. Make a base class have A and B inherit from it.
2. Duplicate code by writing a duplicate method.
3. Dependency injection, modify B so that it takes A and Does some crazy stuff:
class B:
def __init__(self, x, a):
self.a = a(x)
self.x = x
def addOne(self):
self.a.addOne()
self.x = self.a.x
def addTwo(self):
self.x += 2
self.a.x = self.x
All three options are bad for various reasons. There is no option where I can just add a method to B without modifying any other part of the program or duplicating code.
AddOne is not reuse-able because it's tied to mutable state and the constructor and all the methods that share that state. This happens regardless of whether or not it's A.addOne() or addOne(A).
The only universe where addOne can be reused is if it started off as a stateless function (addOneFunc)
def addOneFunc(x):
return x + 1
class B:
...
def addOne(self):
self.x = addOneFunc(self.x)
By making addOne a functional concept I am able to impliment B.addOne without modifying anything else and reusing code to maximum effect. You cannot do this easily with OOP and this is why OOP is bad.
If I want to deal with mutable state, it's actually not technically FP anymore. Inevitably I will have to and that's why many FP paradigms segregate IO with FRP patterns or the IO monad. Even still straight up procedural programming with mutability is still better than OOP because addOneFunc:
def addOneFunc(int& x):
x += 1
is more reusable than addOne:
class Jungle:
def __init__(self, x, gorilla, banana):
self.x = x
heavy_computation()
self.jungle = create_jungle(create_gorilla(banana))
def addOne(self):
self.x += 1
“… Because the problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle. “ —Joe Armstrong
Many people will say don't design your programs this way. Well nobody has the foresight to know exactly how their primitives will compose for the design they have now and in the future. With OOP you will inevitably design your program into a wall because when it comes time to reconfigure the bricks in the wall you realize that OOP has super glued everything together. What happens IRL is that people eventually hit more complex versions of the example problem I gave you above.
- 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”.