Isn't this statement the same? You don't get into the details of who these experts are, what they like about OOP. You summarize the issue because that's what humans end up doing rather than writing a whole essay about what's so great about OOP. Also you're communicating with someone who you presume already knows the details. You're a parody of your own statements.
You create the opposite fashion trend to hate on people who hate OOP which I see plenty of nowadays. Literally half the posts on this thread follow your ideology:
"I don't fully understand either paradigm so rather then picking the fashion trend I decide to make up a new one and be one of those people waving white flags and calling everything apples and oranges because I can't figure out which is worse or which is better."
Since you're unable to find one paradigm better than the other you think others are just being opinionated on the details, so you declare: It's All the Same! Everything is equal! This is pure fantasy. OOP does not stand on equal ground to FP or even Procedural programming. It's like comparing Hitler to Mother Teresa, not all things in this universe are equal.When you look deeply you will find that there IS absolutely something wrong with OOP. It's not a little detail thing. There is something fundamentally wrong with OOP. Not only that, but you will find that the "experts" in CS have largely discounted OOP.
I can assure you the "experts" largely discount OOP by a huge margin. OOP doesn't even have a formal definition.
By experts I mean academics who are experts in programming language creation using formal and mathematical theory. Someone like Martin Fowler is not an expert because all Martin Fowler does is classify concepts with qualitative descriptions and big words.
Let me clarify. An example of an expert in PL theory is someone who can create a programming language that can prove itself correct. Essentially programming languages that are not only type safe, but logically safe to the maximum degree. Idris is an example of one of these programming languages. Imagine writing in one shot, no incremental testing a 9000 line program with zero bugs. Studies in formal verification enable this with cost... and people who know this stuff are an example of experts in the field.
An example of someone posing to be an expert is someone drawing 3 boxes and using lines to connect it to a single parent and calling that the "Trinarium Pattern" in order to sound like an expert. This is largely what "OOP experts" are including Martin Fowler.
You don't believe me? Why don't you try to find a book on the formal theory of OOP. One that starts off with the axiomatic foundations of OOP. You want to know why "lambda calculus" books exist but there are no "Object Oriented Calculus" books? Because there is no formal theory of OOP period. It literally doesn't exist among "experts."
If anything this post should at least point you or someone in the direction of why OOP is just bad. OOP doesn't have formal foundations... no one has bothered to turn it into a theory or science because it's just that bad.
For a person trying to decide if they should learn OOP or not, I believe the above statement (along with the entire comment) is exactly what parent comment is talking about. A "noob's" reaction to that statement would be "what does that mean?", "why should I care?", "is this important?".
For other "noobs", the reaction would be "HN user leafboi seems an expert so I would stick to what he/she says even if I don't understand what he/she means and I will quote him/her every time an OOP discussion comes up. Fuck OOP".
Meanwhile there are still millions of projects done with OOP, including super-complex AAA games, and more are being done right now...
I've heard that OOP in game engines is in decline with so-called "Data Oriented Programming" taking over, that Unity's inheritance class library was a disaster, etc.
So, it's not FP per se, but a focus on data over "objects" is a big part of FP being about "values" instead of opaque state.
Is it small wonder that OO languages are more popular when the vast curriculum of the current generation of programmers were raised on python or Java?
A noob might pick what to learn based off of what I'm saying (learn OOP btw, noob, it's bad but it's pervasive) but he's not going to quote something he doesn't understand or agree with.
>Meanwhile there are still millions of projects done with OOP, including super-complex AAA games, and more are being done right now...
OOP is not bad because you can't make things with it. It's bad because OOP abstractions do not add any real benefits while hindering reusability. That's all.
You can still build plenty of things with OOP. It's like javascript. A universally agreed upon bad language with stupid gotchas made in a week but pervasive due to circumstance.
I like encapsulation of mutable state the most.
Almost all software needs to read and write files, as streams of bytes. The state of the open file handle is incredibly complicated, besides the obvious size + offset it includes effective permissions, OS caches, inodes (linux) / NTFS (windows), and progressively more details on each level of abstraction.
It’s not practical to expose all that stuff to a user who only needs to open a file and write a few bytes. OOP allows for nice abstraction over that mutable state. Here’s an example for C++ or C#: https://docs.microsoft.com/en-us/uwp/api/windows.storage.str... Conceptually, procedural languages like C are doing that too for files, they’re just writing object.Verb as object_verb(Handle instance).
You might reply “files are already implemented by OSes, no one is implementing them” and you would be correct. Still, many real-life I/O problems are conceptually (from software design viewpoint) similar to writing bytes into file handles.
In networking-related code it’s not uncommon to layer multiple levels of network protocols on top of each other, OSI-like https://en.wikipedia.org/wiki/OSI_model Hiding implementations of lower levels of the stack from consumers on higher levels simplifies a lot of things everywhere. OOP with interfaces is an idiomatic way to express that thing in programming languages.
The benefits are collaboration (once the interface is designed, two persons/teams can independently work on implementation and consumer), testability (possible to replace the implementation of lower levels with a mock to test just higher levels), runtime dispatch (possible to build the protocol stack dynamically, e.g. either insert or skip the encryption), and modularity (different levels of the stack can be implemented in different DLLs, even by different companies).
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 ArmstrongMany 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.
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”.
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.
What about React? They say right on their front page: “Build encapsulated components that manage their own state, then compose them to make complex UIs.” That’s a textbook definition of OOP.
> it is better to deal with it without using OOP
That’s a famous example but why do you think it’s always better? Sometimes, like when dealing with graphs, holding the jungle with the banana is exactly what you want.
> If the focus of your code is reusability and modularity
My primary focus is meeting requirements and budget. All technical decisions about reusability, modularity, performance, and the rest of them, are driven by these two.
Some projects don’t need reusability at all, because that’s research, or a proof of concept, or a video game that won’t be maintained once launched.
In other cases, being too aggressive about eliminating duplicate code can harm long-term maintenance. More likely to happen in higher-level parts. You introduce an abstraction, then you need to handle just one special case where the code being reused needs to behave differently and introduce a parameter. Repeat a few times and you now have tons of parameters, flags, and other crap no one understands, not even you. This happens regardless on OOP/FP/procedural, the crap is different in these cases but its effect on maintenance cost is equally bad. Wouldn’t happen if you would be more conservative about introducing abstractions, and instead copy-pasted a few lines of code here and there.
> for most modern programming the performance cost is no longer a factor that matters
Almost everything has a performance budget. For a web app difference between 1ns and 1ms doesn’t matter at all, but difference between 100ms and 1 seconds still matters, users will complain, search engines will downrank. Desktop apps ideally need to respond within 17ms because mainstream displays render at 60Hz. Performance cost of immutability is huge, can be 1-2 orders of magnitude, for 2 reasons. (1) memory allocations are relatively expensive (2) CPUs have cache hierarchy, main memory is like 20-40 times slower than L1D cache, each time you allocating a new immutable object it’s pretty much guaranteed to be out of caches.
Still, there’re many cases when immutability is fine. C# immutable structs are awesome, unlike classes they’re free in terms of memory allocations, and they are usually on stack which guarantees they’re well cached. The upcoming C# 9.0 has a language support for immutable classes, pretty sure I’ll use it a lot once released and debugged.
> Games and Databases are two examples where FP may not be the most appropriate paradigm due to the performance critical nature of the areas.
Also CAD/CAM/CAE, realtime multimedia, some projects in embedded esp. real-time stuff. By the way, these areas are what I mostly do for a living.
> If your class does not hold mutable state then essentially it's just a class repurposed as a namespace with syntactic sugar
I like that sugar, and using that approach quite a lot in my code. For example, private methods and fields are invisible from outside, makes life easier because they don’t show in intellisense. I disagree that OOP promotes mutable state for cases when it’s not needed, it all depend on the language. JavaScript or Python don’t support immutable properties of their objects, but C# and (to lesser extent, but still) C++ do.
However, the real value of OOP is when you have to deal with complex mutable state. Every single time mutable state is essential to the problem (rich GUI is just one area where it’s the case, there’re others), OOP has no good alternatives.
I made a mistake here. Not react. Redux. Basically the DOM update loop that's popularly associated with languages like elm and frameworks like react. That world changes so fast I'm not even sure if Redux is relevant anymore.
>My primary focus is meeting requirements and budget. All technical decisions about reusability, modularity, performance, and the rest of them, are driven by these two.
Then do what you need just know that OOP doesn't contribute to reusability. I actually don't see any benefit to is over just procedural programming.
>Almost everything has a performance budget. For a web app difference between 1ns and 1ms doesn’t matter at all, but difference between 100ms and 1 seconds still matters, users will complain, search engines will downrank. Desktop apps ideally need to respond within 17ms because mainstream displays render at 60Hz. Performance cost of immutability is huge, can be 1-2 orders of magnitude, for 2 reasons. (1) memory allocations are relatively expensive (2) CPUs have cache hierarchy, main memory is like 20-40 times slower than L1D cache, each time you allocating a new immutable object it’s pretty much guaranteed to be out of caches.
Nah most CPU stuff is so fast none of this matters anymore. For web development, (I'm a web developer) They use languages like python with garbage collectors to handle stuff. The reason is because the bottlneck lies in IO. Python is slow but the database is slower and that's where they use a procedural language like C. For browser stuff they use javascript now and the DOM and the redux pattern so pure performance is no longer as critical because stuff is so fast anyway. I mean really when the browser is loading you're waiting on IO, not on rendering or javascript.
>Also CAD/CAM/CAE, realtime multimedia, some projects in embedded esp. real-time stuff. By the way, these areas are what I mostly do for a living.
That's similar to gaming right? Graphics. So essentially yes you do work in an area where FP is probably not the best choice. However the majority of programming nowadays is Web sites. Even so JS and functional programming is powerful enough to drive 3D graphics on web browsers.
>However, the real value of OOP is when you have to deal with complex mutable state. Every single time mutable state is essential to the problem (rich GUI is just one area where it’s the case, there’re others), OOP has no good alternatives.
What's the difference between a function that modifies state and a method that modifies it's own internal state? Almost none except the method is tied to context. The method cannot be used outside of that context.
You have to instantiate state and irrelevant context to use a method and that is the problem. The alternative to OOP is to just use procedural programming because literally there is only one single difference that separates the two paradigms:
Methods are tied to context, functions are not.
In knowing this you will know that basically with OOP you're tying your hands together for no good reason. You can write those exact same methods as separate procedural functions to get the same functionality without the downsides.
> I disagree that OOP promotes mutable state for cases when it’s not needed, it all depend on the language. JavaScript or Python don’t support immutable properties of their objects, but C# and (to lesser extent, but still) C++ do.
It promotes it by virtue of being the only feature that separates a class from a namespace. If you're not using a mutable variable in your class you're not doing OOP because what you're doing is isomorphic to writing a namespace with functions. (You can have private functions in namespaces for certain languages to btw.)
As I side note to easily about the FRP pattern you should just checkout ELM. https://elm-lang.org/examples/first-person. It's a batteries included functional programming framework that you can program right in the browser. In the example I gave you the first person 3D program is written by a user without the user ever writing a procedure or a method that modifies state. All the functions minus the shader stuff are strictly pure and therefore reusable.
Have very little idea, I don’t do web development.
> That's similar to gaming right?
There’re differences e.g. games render at 60Hz, games don’t run CPU-bound jobs which load all cores of all CPUs for minutes to hours. But overall, indeed it’s much more similar to games than to web.
> Graphics
I would estimate graphics is 25% of code complexity in games (however most modern games are using off the shelf engines who implement major parts of that), and 10% of complexity of CAD/CAM.
> the majority of programming nowadays is Web sites
I think the majority of programmers nowadays, and have been for decades, working on internal line of business applications. No reason to generalize their experience over the rest of the software development.
> Even so JS and functional programming is powerful enough to drive 3D graphics on web browsers.
JS is merely passing data to WebGL APIs. These APIs are implemented in web browsers, and underneath browsers in GPU drivers. Both modern browsers and GPU drivers have millions of lines of code written in C or C++, and JS is not performant enough to replace these, not even close.
> What's the difference between a function that modifies state and a method that modifies it's own internal state?
Visibility of that state. Usability of the API. Composability of the overall thing.
> The method cannot be used outside of that context.
Yes, and that’s usually a good thing. Here’s a method: https://docs.microsoft.com/en-us/uwp/api/windows.storage.str... You can’t write bytes to a stream without the stream. From API design perspective that’s a feature, not a bug.
> What's the difference between a function that modifies state and a method that modifies it's own internal state?
For some cases, runtime polymorphism helps a lot. In that case, neither you nor compiler knows what’s in the state or how’s that implemented, it only determined in runtime. Take a look at this toy API in C#:
https://gist.github.com/Const-me/0745e09695e8e8615c14b18fc3f...
The internal state of the object returned from setupConnection method can be just the TCP socket, or it can be a socket + AES decryption + a decompressor.
The decrypt, decompressGzip and decompressLz4 functions are usable for any streams not just TCP sockets, you can pass them a file or any other stream and they’ll work just fine, despite their internal state changes to something completely different.
Then I would say you are a bit behind the curve when it comes to what the majority of people are doing in UI development. The majority of UI development happens on the web. Current practices involve using a functional paradigm to deal with mutable state. The pattern is called Functional Reactive Programming. FRP for short.
>I think the majority of programmers nowadays, and have been for decades, working on internal line of business applications. No reason to generalize their experience over the rest of the software development.
I'm generalizing over a vast majority. The vast majority of SW development is web stuff and basically in web development performance is not the priority. Useability is. Basically people choose languages and frameworks like ruby, php and python over C++ for ease of use. Software development bootcamps emphasize javascript not C++.
These languages are so slow that it doesn't matter if you follow the functional style or not.
>JS is merely passing data to WebGL APIs. These APIs are implemented in web browsers, and underneath browsers in GPU drivers. Both modern browsers and GPU drivers have millions of lines of code written in C or C++, and JS is not performant enough to replace these, not even close.
Yeah I know. Don't write your OS or OS drivers in javascript. In general the platform is implemented by one guy and everyone else operates in his universe. Most web developers and even game developers won't get into the internals of OpenGL.
>Yes, and that’s usually a good thing. Here’s a method: https://docs.microsoft.com/en-us/uwp/api/windows.storage.str.... You can’t write bytes to a stream without the stream. From API design perspective that’s a feature, not a bug.
You missed the point. Object.write is worse than write(stream) because Object can hold more things than just a stream. If you implemented Object.write instead of write(stream) then your write method is forever tied to irrelevant context. Again see the banana gorilla jungle example.... literally the addOne method cannot be reused without creating a banana, a gorilla, and a jungle.
If you want to restrict your function to operate on certain data types you do this by restricting the input parameters. That's it. There is no need to attach it to irrelevant context when it doesn't even really touch other parts of the state.
>Visibility of that state. Usability of the API. Composability of the overall thing.
In FP you hide private state in functions. It is hidden anyway. "C" below.
A -> function(A){return B(A, C)} -> B (C is hidden state)
If you think about your programs this way where hidden state is ephemeral you never will have objects that are in a state of half correctness. The object only has two states: Existence or non-existence, never a state where only half of it's properties are initialized. The hidden state is forever stored within the function expression, ephemeral and inaccessible as it should be.This is 100x better than the explicit use of "private." Hidden state in this case is truly hidden by structure rather than by syntactic tags.
Useability is equal. As you yourself as said object.verb() is semantically equivalent to verb(object).
Objects are not more composable than functions. The only way to compose objects are through inheritance, (which is bad) and object composition which requires the parent object to be aware of the child Object.
Example:
class B():
....
class A()
def __init__(self, B)
self.b = B
A is aware of B, therefore A is not modular. You have to customize A to be aware of B to compose it with B.
universal function composition:
def compose(f: A->B, g: B -> C): A -> C {
return function(x: A){return g(f(x)); }
}
def addOne(x: int){
return x + 1;
}
def timesTwo(x: int){
return x * 2;
}
addOneThenTimesTwo = compose(addOne, timesTwo)
addOne is not aware of timesTwo, timesTwo is not aware of addOne, compose is universal meaning it works on any function of type A -> B.
In terms of composition. Combining functions to form other functions hands down beats combining objects to form other objects. Literally no contest. It's one of the defining features of FP. All your FP primitives are literally tiny modular bricks that your compose into data pipelines that pump data from Input to Output. It is at the two ends of this pipeline where the functional style breaks down and you have to either start breaking rules or using complex abstractions to deal with IO and mutable state.>The internal state of the object returned from setupConnection method can be just the TCP socket, or it can be a socket + AES decryption + a decompressor.
These are all static and stateless IO functions. You're not really doing OOP without an internal mutable var. You could literally rename that class to namespace. (I'm not too familiar with C# but I'm assuming the use of the word static means that the function is not tied to instantiated state meaning all state arrives in as a parameter negating the need for using the word "class" over "namespace")
Worked with that in .NET a bit, specifically this one: https://github.com/reactiveui/ReactiveUI Not a big fan. For example, when I wanted to fix a bug, put a breakpoint to the code that failed, and reproduced, there was no way to tell who or why sent that event, the original context was already lost. For this reason I prefer simpler ways with interfaces or lambdas. Not sure if that’s a problem of the approach or the implementation I used. But generally speaking marshalling contexts is complicated and is a common source of bugs, YMMV.
> in web development performance is not the priority. Useability is.
I can see how it can be the case on servers (one reason is economy, for many companies it makes more sense to rent more EC2 instances instead of writing better software), but I’m not sure same applies to client. In the browser, you have the same 16.6ms performance budget for optimal usability. The fact that JavaScript is generally slower than compiled languages makes things worse.
> literally the addOne method cannot be reused without creating a banana, a gorilla, and a jungle.
Every design pattern can be abused, and none of them is a silver bullet. If you want to reuse that code, make it a global function, all OO languages have them or equivalents (static classes in C#). But other times, the feature if very useful. See the example linked in my previous comment (I hope C# is readable enough even for non-users), the iReadStream object returned from setupConnection can potentially hold quite a lot of referenced things, GZIP compressor, AES implementation, yet for the user of that object it’s just a trivially simple interface with a single read() method.
> If you want to restrict your function to operate on certain data types you do this by restricting the input parameters.
Due to polymorphism, OOP allows to write functions operating on data types unknown to either compiler or the programmer writing that code, as long as the types implement the expected functionality.
Microsoft replaces both data types and code of their IOutputStream.WriteAsync method with windows updates. Their changes don’t break my code, they don’t even require me to recompile it.
For that particular OS kernel API, you might say we aren’t re-implementing it on our side, and you would be correct. Still, that level of flexibility is very useful for making software. At least once the software complexity grows past some threshold: for simpler stuff it often does more harm than good.
> This is 100x better than the explicit use of "private."
These things are orthogonal. No one forces you to return half-initialized objects, you can throw an exception instead.
> Combining functions to form other functions hands down beats combining objects to form other objects.
Not every object can be expressed as a function. That C# example I linked above can, because iReadStream interface only has a single method, but if we want to do the same for writing, we now need 2 methods, write() and flush().
> You're not really doing OOP without an internal mutable var.
1. Why not? We have objects, they have methods which mutate their internal state. Looks OOP enough in my book?
2. If I would have actually implemented these objects instead of writing // TODO comments, these implementations all would have a fair bit of internal mutable vars, due to buffering, AES blocks, and other shenanigans.
The tooling is irrelevant to the paradigm itself as tooling can improve over time. Either way it's the dominant pattern right now for UI. I'm not an expert in it but I do know that the tooling for react/redux right now is quite good.
>I can see how it can be the case on servers (one reason is economy, for many companies it makes more sense to rent more EC2 instances instead of writing better software), but I’m not sure same applies to client. In the browser, you have the same 16.6ms performance budget for optimal usability. The fact that JavaScript is generally slower than compiled languages makes things worse.
In servers it's not a priority because the database is the bottleneck. Servers need to just be faster than the database and that can easily be achieved with something slow like ruby or horizontal scalability. The pattern in web development on the backend is highly similar to functional programming. Web servers are mostly stateless and state is stored in a database, so you can basically increase the amount of servers that access the database for basically an unlimited amount of capacity for parallelism. Of course all of this is bottlenecked by the speed of your database.
For the front end it's actually less critical, believe it or not. Even the shittiest phone is already plenty powerful. Most of the slowness in this area is still in IO or data transfer. All those loading screens you're waiting on is mostly data transfer and database bottlenecks. The reason why UIs are so slow nowadays is because literally half of most UIs live on the cloud and are downloaded by your browser/mobile device. We are well past the time when UIs take 16.6ms to load. 1s is actually quite good nowadays.
>Every design pattern can be abused, and none of them is a silver bullet
Except I didn't abuse a design pattern. I used an incredibly general example. Basically a single method in a class will likely not be utilizing all members in that class yet at the same time you cannot use a method without instantiating ALL members. it's a pointless restriction that is solved by changing the method into a function that operates only on its input parameters.
When something is wrong with the most trivial of all examples... then something is wrong with the entire paradigm.
>If you want to reuse that code, make it a global function, all OO languages have them or equivalents (static classes in C#).
Which is another of saying "use functions in namespaces instead of objects." I recommend that if you're doing C# or JAVA you should write all your functions this way. There is no reason why you shouldn't have this level of re-usability on ALL your code. There's no point in making things methods tied to random state.
>See the example linked in my previous comment (I hope C# is readable enough even for non-users), the iReadStream object returned from setupConnection can potentially hold quite a lot of referenced things, GZIP compressor, AES implementation, yet for the user of that object it’s just a trivially simple interface with a single read() method.
I looked at it, the use of static keyword makes it basically plain old functions rather then methods. I wouldn't call it OOP. But whatever it's just words. Basically I'm saying: "shared mutable state in classes offers no benefits and makes your program less modular" You can still call it OOP if you want but if you agree with my statement then overall we're in agreement.
>Due to polymorphism, OOP allows to write functions operating on data types unknown to either compiler or the programmer writing that code, as long as the types implement the expected functionality.
This is just a type class or an interface or whatever you want to call it. It exists in other languages as differently named concepts. I would say it's not an OOP thing. An untyped language like python basically every single variable type in that language is Polymorphic to each other.
>Microsoft replaces both data types and code of their IOutputStream.WriteAsync method with windows updates. Their changes don’t break my code, they don’t even require me to recompile it.
That's because you already bent your code to fit their interface. They maintain that interface and you don't have to change anything. This is universal to all types of programming paradigms FP included.
>These things are orthogonal. No one forces you to return half-initialized objects, you can throw an exception instead.
In an FP language you are forced to never even be allowed to have half-initialized objects exist. The concept doesn't exist in FP. This eliminates a whole class of potential errors from your program.
In procedural languages a common pattern I see is this:
var x = A();
x.a = 1
x.b = 2
....
Basically if the procedures are written out like that it leaves room for a potential error/mistake/bug to occur. What if you miss instantiating something?In a functional language it must be done only like this:
var x = A{1, 2, ...}
And most FP languages do not allow nulls or future modification. Meaning you have to figure out the true value of every variable and shove it into the object during instantiation. These are essentially procedural or temporal errors that functional programming gets rid of by eliminating the concept of time. It asks you to define the correct piece of data rather than treat it like a container that you fill with data over time.The language Elm that I recommended earlier is an FP language that takes concepts to the extreme. In ELM you can absolutely never have a runtime error (other then blowing up memory). It is impossible because the language doesn't allow you to do stupid things that would otherwise trigger runtime errors in other languages like C# or C++. It eliminates a whole class of errors you find in C, C++ or C#. The only thing you have to deal with is logic errors.
>1. Why not? We have objects, they have methods which mutate their internal state. Looks OOP enough in my book?
You referring to your example? I dunno it looks to me like they're static meaning they don't mutate internal state. Do those functions in your example modify variables that aren't part of the parameters? I think I'm being liberal with my use of "mutate internal state." In this case I mean a member variable of the class. You labeled it static and therefore the function is basically just a procedural function. I wouldn't call it OOP anymore then I would call a function in C OOP.
>2. If I would have actually implemented these objects instead of writing // TODO comments, these implementations all would have a fair bit of internal mutable vars, due to buffering, AES blocks, and other shenanigans.
Well they aren't mutating global vars are they? If it's just mutating scoped variables within the function then it's still not OOP in my book. It has to modify a variable outside of it's scope that's part of the instantiated class. I wouldn't call this function FP either. It's definitely procedural programming similar to C or golang.
To make it clear I'm saying Procedural programming > OOP and FP > OOP and it's still up in the air for procedural programming vs. FP.
Also the in the banana gorrila example I compared a class to a function.
def addOneFunc(x):
x += 1
Just so you know the above example is not an example of FP. That is a procedural function that mutates state. I was using it to show you how even Procedural programming is better than OOP.Yes precisely. I'm not trying to blame experts or people learning from them, I'm just describing the phenomenon, too which I am not exempt from. I'm just as much a part of it as everyone else, and I've been in both sides, the expert side, and the learner side.
I try to be more aware of it now, and tone down my statements or be more precise on the extent of my criticism, when in the expert side. And when in the learner side, I try to be more skeptical of blanket statement, and not to take everything at face value, to look for the details and read up on the nuances so I don't distort and bias my understanding.
Understanding that phenomenon explains why even if it appears as if everyone hates OOP, it is still widely spread and in use, basically because it's much more complicated then that.
I won't address the rest of your comment, because I'm actually a functional programmer in my day job, even though I've done ton of OOP prior. I prefer FP, but in reality, I prefer the convergence of good ideas which many modern languages are bringing, and for whom the ideas come from OO and FP alike.
Edit: Okay, I will address one thing. An expert in PL theory is not an expert at writing programs or at engineering business solutions that leverage the known science of computers.
Who cares. I don't think n00bs are taking what you say and running with it without understanding it. It's not like they're going to quote something they don't understand. The hatred for OOP is from a genuine belief that they understand the concepts (and so is the love.) If anything what you say could only change the direction of what they choose to learn. I doubt they'll quote or promote anything they don't fully understand.
>An expert in PL theory is not an expert at writing programs or at engineering business solutions that leverage the known science of computers.
Nobody is an expert at this because there's no formal theory behind "business solutions" It's all people making shit up and walking in circles. Is your arbitrary solution the best? Is someone else's better? Maybe your solution is just mediocre how do we rate any of this what exactly is a "good business solution?" This whole field is largely an art.
So when a field is an art you get people liking terrible drawings done by a three year old, take a look:
https://i.pinimg.com/236x/5f/c9/4f/5fc94f54df98d50bfae9b1807...
Wait I lied it's Pablo Picasso. This work is the work of a genius according to most people. But you can definitely see why from other angles it's considered pure crap. Is this guy an expert or is he on drugs? Maybe both, nobody knows... same with business solution architects.
Because nobody can formally define "good business solutions" most of these "business solutions" including OOP is just like that piece of art above. It's all opinion and people making shit up.
So just discount it all. Modern art to me is a form of fraud. And so is a business solution architect or an OOP architect. Follow the science, follow what's definitive.
See Luca Cardelli, A Theory of Objects. I might agree with you that the formal foundations of OOP are far from intuitive or foolproof (which is why everyone cautions you against using, e.g. implementation-inheritance unless you know exactly what you're doing!) but they do exist.
This book has like three reviews on amazon and in general a formal theory doesn't exist. You won't find a formal theory about OOP calculi on wikipedia.
There are thousands of people in academia and of course at least one of them will attempt to formalize OOP. But largely said and done, overall there is no formal theory that is well accepted. Just random arbitrary attempts at formalization.
https://pages.cpsc.ucalgary.ca/~robin/FMCS/FMCS2014/An%20int...
https://en.m.wikipedia.org/wiki/Luca_Cardelli
There's also definitely a lot of published papers about typing OO, like https://ieeexplore.ieee.org/document/316056 , https://ieeexplore.ieee.org/document/287603 , https://drops.dagstuhl.de/opus/volltexte/2016/6103/
I'm also not really sure what you're trying to point out. When you talk about a calculi for FP, you're talking about what? Lambda Calculus?
If so, the base mathematical model of OOP is the Turing machine. That's the model of computation involved. It's been extensively researched and is absolutely well accepted and recognised by everyone everywhere.
Similarly to how there was no mathematical model of Haskell or Idris, etc. There wasn't any for OOP languages either like Java, C++, etc. Any attempt at a mathematical framework over those languages came after, and none of them have a strong one that I know off. Some of them, maybe most especially Idris, did get a lot of inspiration from known typing mathematical models, but still the language itself does not have a full on calculus of itself (correct me if I'm wrong)
If you're talking about type theory and type systems as related to provability of computation, that's like a whole other conversation. But even then, there's ton of literature that research type systems that can prove OOP, often focused on subtyping since that's the main typing discipline of most OO languages with static type systems.
Here is a nice exercise, an FP calculus notation about the semantics of type classes in Haskell.
Afterwards, it would be nice to see the lambda calculus for functors in OCaml.
I'm obviously not trolling. You literally admitted you are. I can't flag you, so I'm going to plant a word here that will hopefully attract attention and get some admin to stop you. Stop trolling or fuck off.
I asked for the papers describing specific semantic models of two mainstream FP languages to give you the opportunity to prove your point, instead you threat to flag me, I think Dan will take his own conclusions.
That's my opinion. That's not called trolling. Disagreeing with people respectfully is not called trolling. Saying the shit you said above: that you're out of food to continue trolling me IS trolling.
I'd flag you already but you need a certain rep to do that. I'm quite new.. but let's see if Dang actually does his job.
Thank you for demonstrating my point :p
This is exactly what I'm referring too. The use of hyperboles or blanket statement by experts, which are taken at face value by those learning from them. Whereas your real sensibilities are well reasoned, nuanced, and your disliking of OO is in the details. Not everyone can get that from your reaction. So it creates this fashion trend, it's trendy to hate on OO. That's not constructive, because experts also know there's a lot to learn from OO, and not everything about it is bad.