My brain can't handle OOP anymore(swizec.com)
swizec.com
My brain can't handle OOP anymore
http://swizec.com/blog/my-brain-cant-handle-oop-anymore/swizec/4320
19 comments
>I have to say, having stared in to the abyss of large Haskell code bases, they're not necessarily easier to read than a large Java code base.
Actually a Haskell oneliner can be more challenging that a large Java code base.
Actually a Haskell oneliner can be more challenging that a large Java code base.
And a single Java class can be more challenging than a large Haskell code base. So what?
So the first tends to happen far more often than the second.
If anything, Java code is boring and boilerplate like, not difficult to comprehend.
Haskell code on the other hand, not so.
If anything, Java code is boring and boilerplate like, not difficult to comprehend.
Haskell code on the other hand, not so.
I think we're arguing over subjectivities here -- comprehensibility is entirely dependent on the people involved as much as the language in question. If you're better at Java than Haskell, you'll find it easier to follow, and vice-versa; I know people in each category. And you can write clearly or obfuscate in either language; I've seen all the combinations.
Personally, I tend to find Haskell easier to follow. I suspect that's mostly because I've used a lot more Haskell than Java of late, but it'll also be a matter of personal taste. As you say, Java can be very boilerplate-heavy -- some may find the redundancy makes it easier to follow, as it can remind you what's what "as you go"; others may feel like they're scrolling up and down hunting for meaningful code, and find all that boilerplate a distracting waste of space.
I will admit that, on average, I'll have to spend a bit longer looking at a line of Haskell than a line of Java before I know exactly what it does -- but then again, you rarely need anywhere near as many lines of Haskell as Java to solve the same problem, so it feels like a net gain to me.
Personally, I tend to find Haskell easier to follow. I suspect that's mostly because I've used a lot more Haskell than Java of late, but it'll also be a matter of personal taste. As you say, Java can be very boilerplate-heavy -- some may find the redundancy makes it easier to follow, as it can remind you what's what "as you go"; others may feel like they're scrolling up and down hunting for meaningful code, and find all that boilerplate a distracting waste of space.
I will admit that, on average, I'll have to spend a bit longer looking at a line of Haskell than a line of Java before I know exactly what it does -- but then again, you rarely need anywhere near as many lines of Haskell as Java to solve the same problem, so it feels like a net gain to me.
>I think we're arguing over subjectivities here -- comprehensibility is entirely dependent on the people involved as much as the language in question. If you're better at Java than Haskell, you'll find it easier to follow, and vice-versa; I know people in each category.
I maybe arguing over subjectivities, but subjectivities turn into objectivities with the tool of statistics.
And I think that in general (statistically), people find Haskell more difficult than Java.
I maybe arguing over subjectivities, but subjectivities turn into objectivities with the tool of statistics.
And I think that in general (statistically), people find Haskell more difficult than Java.
Yeah I think you missed it. "Answering this simple question requires knowing everything. The whole execution history of foo. The whole codebase. You name it, you have to keep it in your head."
Here's the thing about OOADP (Note I didn't just use OOP): it gives you a simple methodology to solve problems and know where to put stuff. You don't know everything, because the assumption is that the codebase is going too big to know everything. But what you _do_ know is where things go; an organization of nouns. Good large projects have a design theme that's easy to understand and gives you the mental tools to reason about which pieces need your attention at any one time. OOP is all about working on little pieces at a time and not worrying about knowing everything.
It has a lot of problems, but so does every paradigm. I've become a huge functional programming fan over the last couple of years, but I can tell you very frankly that nothing beats OOAD. Nothing. It is a gift from the gods. It allows you to take any problem and make it trivial. Large amounts of wiring will be required.
The thing here is that as you start generalizing OOP, you being making this bastard thing that is part OOP and part FP. Or your codebase runs off the rails and becomes a hodgepodge of junk. Or you enter into the "Kingdom of Nouns". The ways to screw up are legion.
Dependency issues are also a freaking nightmare in OOP. That's why OOP and some sort of TDD go hand-in-hand.
I'm mostly with the author, but I don't think he understands the full extent of what he's talking about. OOP (and TDD) is a great tool for large, complex, shared codebases. Even though I'm a huge fan of FP, I remain very skeptical that it can scale well to 100+ developer efforts. I think to truly criticize something like this, you should know both how it's awesome and how it sucks, not just how it sucks.
Here's the thing about OOADP (Note I didn't just use OOP): it gives you a simple methodology to solve problems and know where to put stuff. You don't know everything, because the assumption is that the codebase is going too big to know everything. But what you _do_ know is where things go; an organization of nouns. Good large projects have a design theme that's easy to understand and gives you the mental tools to reason about which pieces need your attention at any one time. OOP is all about working on little pieces at a time and not worrying about knowing everything.
It has a lot of problems, but so does every paradigm. I've become a huge functional programming fan over the last couple of years, but I can tell you very frankly that nothing beats OOAD. Nothing. It is a gift from the gods. It allows you to take any problem and make it trivial. Large amounts of wiring will be required.
The thing here is that as you start generalizing OOP, you being making this bastard thing that is part OOP and part FP. Or your codebase runs off the rails and becomes a hodgepodge of junk. Or you enter into the "Kingdom of Nouns". The ways to screw up are legion.
Dependency issues are also a freaking nightmare in OOP. That's why OOP and some sort of TDD go hand-in-hand.
I'm mostly with the author, but I don't think he understands the full extent of what he's talking about. OOP (and TDD) is a great tool for large, complex, shared codebases. Even though I'm a huge fan of FP, I remain very skeptical that it can scale well to 100+ developer efforts. I think to truly criticize something like this, you should know both how it's awesome and how it sucks, not just how it sucks.
But what you _do_ know is where things go; an organization of nouns.
That's what Clojure namespaces provide. Regardless, the mutable state of objects doesn't align well with multi-core concurrency so its thread-lock complexity is rendering it obsolete.
That's what Clojure namespaces provide. Regardless, the mutable state of objects doesn't align well with multi-core concurrency so its thread-lock complexity is rendering it obsolete.
i read your post twice because its the top comment from my view, and while I see you wrote a lot, I don't think you actually said anything. are you interested in clarifying?
Sorry about that.
Here's the underlying philosophy of OOP not covered by the original article [philosophy described]. Here are some strengths not covered by the original article [salient points covered].
Author managed to share his frustration with the structural details of the paradigm without conveying that he understood the bigger picture of its actual effective use.
That's a recap. Work any better for you now? Or did you not understand my description of how the philosophy works or what the strengths were?
Here's the underlying philosophy of OOP not covered by the original article [philosophy described]. Here are some strengths not covered by the original article [salient points covered].
Author managed to share his frustration with the structural details of the paradigm without conveying that he understood the bigger picture of its actual effective use.
That's a recap. Work any better for you now? Or did you not understand my description of how the philosophy works or what the strengths were?
dustingetz's criticism was a bit harsh, but I kind of agree with him. You aren't concretely illuminating how those qualities of OOP (or OOADP) are advantages or are unique to OO. I would really value more clarification from you on this, given your prolific HN presence and experience. I genuinely want to learn more about this (see my other comment in this thread), but I've struggled to find really good, motivating examples in the past.
> nothing beats OOAD. Nothing. It is a gift from the gods. It allows you to take any problem and make it trivial.
I'm supremely skeptical of claims like these. I could say that same thing about... anything. But that's just not enough.
> Or your codebase runs off the rails and becomes a hodgepodge of junk. Or you enter into the "Kingdom of Nouns". The ways to screw up are legion.
So how do you avoid this? How can I develop an intuition about good OO design?
You talk about TDD a bit, but you can do TDD with functional programming too, and in many ways it's easier (less need for mock objects and dependency injection, which are as good an example of incidental complexity as I can think of).
> I think to truly criticize something like this, you should know both how it's awesome and how it sucks, not just how it sucks.
Yes! Please, help me learn how awesome OOP can be, and get to a place where I can criticize OOP more brutally. ;) Seriously though, what open source code or books would you recommend? I looked here:
http://hn-books.com/#BC=0&EC=0&FC=0&Q0=11&QC...
... but it wasn't clear to me which of these books would be best. Many of them are a bit too specific.
The functional programming proponents who have convinced me most are Clojure people. Rich Hickey's simplicity talk, his talks and mini-essays on state and time, Stuart Halloway's similar talk on simplicity, the idea of values over identities, dealing with basic data structures (maps, vectors, sets) over burying data in classes and domain specific APIs, separating out OO features to get "OO a la carte" ... it's very compelling stuff, and most of it is about striving for simplicity and tearing out unnecessary complexity. That's where I'm coming from in my skepticism of OOP, because most of it seems to make things more complex than they need to be, with not a lot of gain.
On the other hand, I'm pretty green, so I know that much of this could just be naive fantasy, since I've never had to deal with massive codebases being hacked on by 100+ programmers, neither in FP or OOP languages/styles.
> nothing beats OOAD. Nothing. It is a gift from the gods. It allows you to take any problem and make it trivial.
I'm supremely skeptical of claims like these. I could say that same thing about... anything. But that's just not enough.
> Or your codebase runs off the rails and becomes a hodgepodge of junk. Or you enter into the "Kingdom of Nouns". The ways to screw up are legion.
So how do you avoid this? How can I develop an intuition about good OO design?
You talk about TDD a bit, but you can do TDD with functional programming too, and in many ways it's easier (less need for mock objects and dependency injection, which are as good an example of incidental complexity as I can think of).
> I think to truly criticize something like this, you should know both how it's awesome and how it sucks, not just how it sucks.
Yes! Please, help me learn how awesome OOP can be, and get to a place where I can criticize OOP more brutally. ;) Seriously though, what open source code or books would you recommend? I looked here:
http://hn-books.com/#BC=0&EC=0&FC=0&Q0=11&QC...
... but it wasn't clear to me which of these books would be best. Many of them are a bit too specific.
The functional programming proponents who have convinced me most are Clojure people. Rich Hickey's simplicity talk, his talks and mini-essays on state and time, Stuart Halloway's similar talk on simplicity, the idea of values over identities, dealing with basic data structures (maps, vectors, sets) over burying data in classes and domain specific APIs, separating out OO features to get "OO a la carte" ... it's very compelling stuff, and most of it is about striving for simplicity and tearing out unnecessary complexity. That's where I'm coming from in my skepticism of OOP, because most of it seems to make things more complex than they need to be, with not a lot of gain.
On the other hand, I'm pretty green, so I know that much of this could just be naive fantasy, since I've never had to deal with massive codebases being hacked on by 100+ programmers, neither in FP or OOP languages/styles.
So whenever I post on OOADP, seems like everybody gets in a bit of a snit with me :) (not you, just a small minority of the community. Perhaps these are dangerous political waters!) But I'll explain a bit more. Please remember, though, that this is long-format material. I have serious reservations about being able to explain myself or convince anyone of anything in an HN thread. But I can certainly share my perspective.
Like I said, I'm becoming a huge fan of FP -- to the point I'm really not interested in doing a lot of OOP in the future, and I did OOP for many, many years. But that's because I work with small teams mostly.
OOP is all about the organization of the ontology of the problem domain. It's about gaining a common understanding of a general organizational philosophy that then can be scaled and coded. In OOAD you start with simple conversations and slowly created complicated structure where everything has a place. It's this process of being able to take any problem and create a complicated place for everything that is both OOP's strength and weakness.
I started with the Tao of Objects http://amzn.to/KHE5TK and the Coad Yourdon books http://amzn.to/IS9fqj and http://amzn.to/IS9oKh. I still like these because they focus more on how to mentally approach OOAD instead of the details of coding. People get lost in the details here instead of learning the key skill: how to think about problems. (which is useful no matter which type of language you use)
The best way to describe OOAD is "applied philosophy". That is, you are taking a world known only to the customer and translating it into machine code. On one hand we have the slipperiness of language and the fuzziness of human thought. On the other hand we have hard science and boolean logic. OOAD gives you a game plan for making that journey in an easy-to-use manner. FP is not like that. FP is more like building a compiler for whatever the problem is. Same result, but the thinking process is completely different. OO is concerned with top-down. FP makes little blocks and then fits them together into bigger units. In OOP you might spend a lot of time on structure without ever writing any code that does anything at all. But when you _do_ write something, it's already broken down into tiny digestible pieces. In FP I always feel like I'm carrying the linker around in my head. :)
In some problems, it's more important to gain immediate functionality than it is to worry about scaling your code or development team. Startups are mostly like this, and FP and startups go very well together. In some problems, however, it's more important to have clear areas of responsibility and an overall plan for life, the universe, and everything. Large government or corporate projects are like this. The organization of the code units into something mirroring the domain model allows planning at a much larger scale.
As I get older I realize that I was a bit of an architecture astronaut in my younger years -- always creating a structure much more complex than was actually needed. But I also note that these projects usually went off without a hitch. We were able to hire "normal" programmers and we were able to hand the code off to "normal" maintenance programmers when we were done. OOP, to me at least, is much more about the people part of programming than it is the technical part. FP guys are always making these technical arguments about their language, and they miss the point entirely: the problem that OOADP solves isn't a technical one, it's an organizational one. Where does stuff go?
So if I had a large project to do, I'd start with OOAD and then when I got into later design think seriously about which parts would deploy better FP or OOP. The browser code is going to need some kind of namespace, which is usually done with objects in Javascript. Talking from the browser to the server looks entirely functional to me. The back-end gets a bit tricky. There's nothing like a normalized database model for data analysis, but creating a business and database tier looks counterproductive in some cases. It'd depend a lot on the circumstances.
Like I said, I'm becoming a huge fan of FP -- to the point I'm really not interested in doing a lot of OOP in the future, and I did OOP for many, many years. But that's because I work with small teams mostly.
OOP is all about the organization of the ontology of the problem domain. It's about gaining a common understanding of a general organizational philosophy that then can be scaled and coded. In OOAD you start with simple conversations and slowly created complicated structure where everything has a place. It's this process of being able to take any problem and create a complicated place for everything that is both OOP's strength and weakness.
I started with the Tao of Objects http://amzn.to/KHE5TK and the Coad Yourdon books http://amzn.to/IS9fqj and http://amzn.to/IS9oKh. I still like these because they focus more on how to mentally approach OOAD instead of the details of coding. People get lost in the details here instead of learning the key skill: how to think about problems. (which is useful no matter which type of language you use)
The best way to describe OOAD is "applied philosophy". That is, you are taking a world known only to the customer and translating it into machine code. On one hand we have the slipperiness of language and the fuzziness of human thought. On the other hand we have hard science and boolean logic. OOAD gives you a game plan for making that journey in an easy-to-use manner. FP is not like that. FP is more like building a compiler for whatever the problem is. Same result, but the thinking process is completely different. OO is concerned with top-down. FP makes little blocks and then fits them together into bigger units. In OOP you might spend a lot of time on structure without ever writing any code that does anything at all. But when you _do_ write something, it's already broken down into tiny digestible pieces. In FP I always feel like I'm carrying the linker around in my head. :)
In some problems, it's more important to gain immediate functionality than it is to worry about scaling your code or development team. Startups are mostly like this, and FP and startups go very well together. In some problems, however, it's more important to have clear areas of responsibility and an overall plan for life, the universe, and everything. Large government or corporate projects are like this. The organization of the code units into something mirroring the domain model allows planning at a much larger scale.
As I get older I realize that I was a bit of an architecture astronaut in my younger years -- always creating a structure much more complex than was actually needed. But I also note that these projects usually went off without a hitch. We were able to hire "normal" programmers and we were able to hand the code off to "normal" maintenance programmers when we were done. OOP, to me at least, is much more about the people part of programming than it is the technical part. FP guys are always making these technical arguments about their language, and they miss the point entirely: the problem that OOADP solves isn't a technical one, it's an organizational one. Where does stuff go?
So if I had a large project to do, I'd start with OOAD and then when I got into later design think seriously about which parts would deploy better FP or OOP. The browser code is going to need some kind of namespace, which is usually done with objects in Javascript. Talking from the browser to the server looks entirely functional to me. The back-end gets a bit tricky. There's nothing like a normalized database model for data analysis, but creating a business and database tier looks counterproductive in some cases. It'd depend a lot on the circumstances.
> So whenever I post on OOADP, seems like everybody gets in a bit of a snit with me :) (not you, just a small minority of the community. Perhaps these are dangerous political waters!) But I'll explain a bit more. Please remember, though, that this is long-format material.
I appreciate it, and I recognize that there's sometimes a bit of knee-jerk snittiness that can come across in these OOP vs FP debates, including in my post. If you've written about this in a longer format somewhere else, or plan to, I would be interested.
> FP makes little blocks and then fits them together into bigger units. In OOP you might spend a lot of time on structure without ever writing any code that does anything at all.
This is getting closer to the crux of things. The bottom-up FP style just fits my own workflow and habits better. Spending a lot of time building structure without writing code that does anything seems like dangerous premature design. Thinking about the problem and about problem solving ahead of time is important, but I'm not sure that building structure is.
> In FP I always feel like I'm carrying the linker around in my head. :)
I'm not really sure what you mean.
> OOP, to me at least, is much more about the people part of programming than it is the technical part. FP guys are always making these technical arguments about their language, and they miss the point entirely: the problem that OOADP solves isn't a technical one, it's an organizational one. Where does stuff go?
I think it's possible to answer this question in a functional program, as another commenter said, Clojure namespaces provide an answer to questions like "where". I think bigger picture design like MVC can be useful here, see ClojureScript One for one (relatively simplistic, but very interesting nonetheless) example.
So I guess if OOP/OOADP is about solving a nontechnical problem, my question would be if we can solve those same social problems while writing FP code? If we want to avoid object oriented hierarchies and graphs that are interconnected and complected, can we still address the social and nontechnical problems you think OO solves? Surely that's possible? It seems that relying on OO to solve these problems drags in an awful lot of baggage (like introducing a lot of mutable state that is problematic for very technical reasons).
Thanks for the pointers to those books, I'll check them out.
I appreciate it, and I recognize that there's sometimes a bit of knee-jerk snittiness that can come across in these OOP vs FP debates, including in my post. If you've written about this in a longer format somewhere else, or plan to, I would be interested.
> FP makes little blocks and then fits them together into bigger units. In OOP you might spend a lot of time on structure without ever writing any code that does anything at all.
This is getting closer to the crux of things. The bottom-up FP style just fits my own workflow and habits better. Spending a lot of time building structure without writing code that does anything seems like dangerous premature design. Thinking about the problem and about problem solving ahead of time is important, but I'm not sure that building structure is.
> In FP I always feel like I'm carrying the linker around in my head. :)
I'm not really sure what you mean.
> OOP, to me at least, is much more about the people part of programming than it is the technical part. FP guys are always making these technical arguments about their language, and they miss the point entirely: the problem that OOADP solves isn't a technical one, it's an organizational one. Where does stuff go?
I think it's possible to answer this question in a functional program, as another commenter said, Clojure namespaces provide an answer to questions like "where". I think bigger picture design like MVC can be useful here, see ClojureScript One for one (relatively simplistic, but very interesting nonetheless) example.
So I guess if OOP/OOADP is about solving a nontechnical problem, my question would be if we can solve those same social problems while writing FP code? If we want to avoid object oriented hierarchies and graphs that are interconnected and complected, can we still address the social and nontechnical problems you think OO solves? Surely that's possible? It seems that relying on OO to solve these problems drags in an awful lot of baggage (like introducing a lot of mutable state that is problematic for very technical reasons).
Thanks for the pointers to those books, I'll check them out.
"... can solve those same social problems while writing FP code? If we want to avoid object oriented hierarchies and graphs that are interconnected and complected, can we still address the social and nontechnical problems you think OO solves?..."
I believe that's where functional OO languages like OCAML and F# come in. Do your OOAD, then construct the code functionally. Once you realize you're not going to refactor, start grouping into classes. Your OOAD helps drive your data structures out. Actual code and functionality drive your class structure.
But what I'm finding is that both FP and OOP code evolves into a DSL; FP more directly and OOP in a more roundabout manner. If that observation holds up, then what we're really talking about with organizing code is creating layers of DSLs, not tiers of functionality. If that's the case (and it's a big "if") then something like a code budget and insistence on DSLs as a desired end-state might be the best way to balance both scalability (in terms of team size and effort) and complexity.
I believe that's where functional OO languages like OCAML and F# come in. Do your OOAD, then construct the code functionally. Once you realize you're not going to refactor, start grouping into classes. Your OOAD helps drive your data structures out. Actual code and functionality drive your class structure.
But what I'm finding is that both FP and OOP code evolves into a DSL; FP more directly and OOP in a more roundabout manner. If that observation holds up, then what we're really talking about with organizing code is creating layers of DSLs, not tiers of functionality. If that's the case (and it's a big "if") then something like a code budget and insistence on DSLs as a desired end-state might be the best way to balance both scalability (in terms of team size and effort) and complexity.
the whole driving force of Clojure and Scala, the thing they were designed for[1], is large, enterprise-class codebases, because functional programming makes it easier to reason about program behavior at scale.
[1] http://www.infoq.com/presentations/Simple-Made-Easy (required watching for someone interested in having an opinion on this subject)
you're still saying an awful lot of unsubstantiated opinions. its not about politics, its just about being objectively wrong. frankly, I and it seems also others, just think you're less informed than you think you are. sorry about the super blunt tone, not sure how else to say this. edit: it's good that you're commenting though, these discussions are healthy.
[1] http://www.infoq.com/presentations/Simple-Made-Easy (required watching for someone interested in having an opinion on this subject)
you're still saying an awful lot of unsubstantiated opinions. its not about politics, its just about being objectively wrong. frankly, I and it seems also others, just think you're less informed than you think you are. sorry about the super blunt tone, not sure how else to say this. edit: it's good that you're commenting though, these discussions are healthy.
How can I develop an intuition about good OO design? ... Seriously though, what open source code or books would you recommend?
A lot of people have suggested different perspectives over the years, and if you want to develop a feeling for when and how to use OO, I would recommend exploring as many of those different points of view as you can.
For example, here are some comments by Alan Kay on his view of OO, which emphasize the message passing and late binding ideas:
http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
You might also be interested to read about the original model-view-controller (MVC) idea as used in Smalltalk, which is like a textbook case study of some of Kay’s OO ideas in practice. (Don’t confuse this with modern Web frameworks that use the term MVC but aren’t quite the same thing.)
For a somewhat different view, perhaps more “heavy industrial” OO in spirit, you could read Grady Booch’s book Object-Oriented Analysis and Design with Applications.
As general knowledge, you could also read Design Patterns and some of Robert C. Martin’s early work on the “SOLID” principles. Then read some criticisms and retrospectives of these works for a counterpoint.
For practical thoughts, I suggest reading books like Scott Meyers’ Effective C++ and Joshua Bloch’s Effective Java. Even if you don’t use those specific programming languages, there are a lot of comments about what works in practice with the kind of OO tools are available in that family of languages. Stroustrup has some interesting comments about use of objects and the role they can play in a language that isn’t purely OO in The C++ Programming Language, though it’s only a relatively small part of a large book.
Likewise, you could try to read about using objects/OO in dynamic languages like Ruby and Python, and about JavaScript, where the flavour of OO is different again. I don’t have specific sources I’d recommend as obvious starting points here, but these languages tend to be discussed a lot more often in on-line forums recently because of the high profile of Web work at the moment.
I’d also recommend studying API design, since a lot of the comments and examples in books or conference talks about this area tend to use OO by default for their case studies. The most interesting single source I’ve come across in this area was a lecture given by Casey Muratori:
http://mollyrocket.com/9438
Ultimately I don’t think there’s any substitute for experience or discussing real programming situations with colleagues, but if you start from some of the ideas above hopefully you’ll develop some kind of frame of reference and a pool of ideas you can draw on in your own work.
A lot of people have suggested different perspectives over the years, and if you want to develop a feeling for when and how to use OO, I would recommend exploring as many of those different points of view as you can.
For example, here are some comments by Alan Kay on his view of OO, which emphasize the message passing and late binding ideas:
http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
You might also be interested to read about the original model-view-controller (MVC) idea as used in Smalltalk, which is like a textbook case study of some of Kay’s OO ideas in practice. (Don’t confuse this with modern Web frameworks that use the term MVC but aren’t quite the same thing.)
For a somewhat different view, perhaps more “heavy industrial” OO in spirit, you could read Grady Booch’s book Object-Oriented Analysis and Design with Applications.
As general knowledge, you could also read Design Patterns and some of Robert C. Martin’s early work on the “SOLID” principles. Then read some criticisms and retrospectives of these works for a counterpoint.
For practical thoughts, I suggest reading books like Scott Meyers’ Effective C++ and Joshua Bloch’s Effective Java. Even if you don’t use those specific programming languages, there are a lot of comments about what works in practice with the kind of OO tools are available in that family of languages. Stroustrup has some interesting comments about use of objects and the role they can play in a language that isn’t purely OO in The C++ Programming Language, though it’s only a relatively small part of a large book.
Likewise, you could try to read about using objects/OO in dynamic languages like Ruby and Python, and about JavaScript, where the flavour of OO is different again. I don’t have specific sources I’d recommend as obvious starting points here, but these languages tend to be discussed a lot more often in on-line forums recently because of the high profile of Web work at the moment.
I’d also recommend studying API design, since a lot of the comments and examples in books or conference talks about this area tend to use OO by default for their case studies. The most interesting single source I’ve come across in this area was a lecture given by Casey Muratori:
http://mollyrocket.com/9438
Ultimately I don’t think there’s any substitute for experience or discussing real programming situations with colleagues, but if you start from some of the ideas above hopefully you’ll develop some kind of frame of reference and a pool of ideas you can draw on in your own work.
Ok, so there are a few comments claiming that this kind of revelation only seems valid because the author hasn't seen OO done right, or has only seen bad OO code. As someone who has gone through a similar shift and feels disenchanted with a lot of the OO code I deal with on a daily basis, but is still very much aware of my own inexperience and naivete, I'm wary of these claims but I'm curious.
So what's some really great, clean, elegant object oriented code that I could read to better understand what makes up good OO design? What are the books one should read to develop a deeper understanding of OO's qualities? It doesn't matter what language or platform they focus on, since I think the fundamentals ought to be independent, and I'm willing to work hard. Rails internals, Django internals? Some Java framework? Or is there some awesome piece of Smalltalk code out there that demonstrates the roots of OO? Is the Gang of Four book still relevant? Where do you go for getting the bigger design picture?
For context, I've done a lot of Java (Android) but I don't think I've been exposed to good large scale design in Java (I've read Effective Java and Java Concurrency in Practice, but both of those deal with the nuts and bolts of programming in the small) Most of my functional programming experience is with Clojure, which is very opinionated and takes a radical approach to OO, and I think the bar has been set kind of high by Rich Hickey.
Please don't tell me to go get a job at Google or your company where the code is beautiful all the time; that's just not fair.
So what's some really great, clean, elegant object oriented code that I could read to better understand what makes up good OO design? What are the books one should read to develop a deeper understanding of OO's qualities? It doesn't matter what language or platform they focus on, since I think the fundamentals ought to be independent, and I'm willing to work hard. Rails internals, Django internals? Some Java framework? Or is there some awesome piece of Smalltalk code out there that demonstrates the roots of OO? Is the Gang of Four book still relevant? Where do you go for getting the bigger design picture?
For context, I've done a lot of Java (Android) but I don't think I've been exposed to good large scale design in Java (I've read Effective Java and Java Concurrency in Practice, but both of those deal with the nuts and bolts of programming in the small) Most of my functional programming experience is with Clojure, which is very opinionated and takes a radical approach to OO, and I think the bar has been set kind of high by Rich Hickey.
Please don't tell me to go get a job at Google or your company where the code is beautiful all the time; that's just not fair.
there are a few comments claiming that this kind of revelation only seems valid because the author hasn't seen OO done right, or has only seen bad OO code
That argument is also known as, “No True Scotsman.” People regularly jeer at it when process proponents defend their practices by saying “Yes, that project failed, but you weren’t doing Waterfall/XP/Scrum/Spiral right.” But there’s no reason why we shouldn’t poke it with a sharp stick when defending a technical argument. If so many people are "Doing OOP Wrong," shouldn’t we have serious reservations about it?
I’m sure that if we pick any practice, we can cherry-pick and find some great code written with it. But what good is it if such examples are rare?
That argument is also known as, “No True Scotsman.” People regularly jeer at it when process proponents defend their practices by saying “Yes, that project failed, but you weren’t doing Waterfall/XP/Scrum/Spiral right.” But there’s no reason why we shouldn’t poke it with a sharp stick when defending a technical argument. If so many people are "Doing OOP Wrong," shouldn’t we have serious reservations about it?
I’m sure that if we pick any practice, we can cherry-pick and find some great code written with it. But what good is it if such examples are rare?
Just as you might dismiss the arguments as "No True Scotsman" you could dismiss the original post as anecdotal evidence. The presence of a fallacy does not directly affect the truth of the point being made.
No, but if all the arguments for and against boil down to anecdotal evidence and fallacies, why are we bothering with a discussion at all?
i've been exploring these ideas myself, and i sort of think everyone is asking the wrong question. its not OOP vs OOADP (or whatever), its a reflection on OOP as it exists today, which for better or worse is mostly java. OOP in java is broken - not because an expert can't wield java effectively, but because someone with only java experience will probably not be able to learn effective OOP on their own, because the things java makes easy - the "opinions" of the language - are often not in line with the most elegant "mental" solution, due to things like no concept of mutability, excessive reliance on objects with state instead of functions on data structures. the things java makes easy become idiomatic, and the things that aren't easy become non-idiomatic... these driving forces create a feedback loop that creates an entire ecosystem of programmers and codebases that are decidedly crappy. Combatting macroeconomics problems requires marcoeconomic solutions; education at the individual level isn't sufficient, we need a way to shift the balance of the feedback loop by creating a mainstream language which influences people towards good code.
Clojure, as you point out, has taken great care to have sensible idioms, Clojure is an awesome language in that simply trying to learn it makes you a better programmer, because Hickey has infused it with his opinions of what is excellent code.
whether or not OOP can be done right isn't an interesting question. I'd rather ask what factors in modern mainstream languages are encouraging poor code, and whether its possible to learn from this and design a language/ecosystem that doesn't suffer from these problems. Scala & Clojure have this as a stated goal.
Clojure, as you point out, has taken great care to have sensible idioms, Clojure is an awesome language in that simply trying to learn it makes you a better programmer, because Hickey has infused it with his opinions of what is excellent code.
whether or not OOP can be done right isn't an interesting question. I'd rather ask what factors in modern mainstream languages are encouraging poor code, and whether its possible to learn from this and design a language/ecosystem that doesn't suffer from these problems. Scala & Clojure have this as a stated goal.
I recommend Domain Driven Design by Eric Evans.
There is an argument that OO is bad because it's so hard to do well. I have yet to be convinced that the same doesn't apply to Functional Programming.
There is an argument that OO is bad because it's so hard to do well. I have yet to be convinced that the same doesn't apply to Functional Programming.
Yes, but the difference is Functional Programmers don't have to actually build anything. They made a well funded mess in the eighties with the AI research, and now they can just feel superior to anybody else while producing a tiny percentage of production code in all fields combined, and not very good or popular one at that.
The most popular or useful thing a functional programmer can build (or has every built, Emacs aside) is his own functional language.
/s
The most popular or useful thing a functional programmer can build (or has every built, Emacs aside) is his own functional language.
/s
I haven't seen a good OO codebase either. I've been working with them professionally for about a decade.
I've seen what I think are good OO APIs. But I haven't found a project with decent code yet.
I've seen what I think are good OO APIs. But I haven't found a project with decent code yet.
This seems to be yet another example of the "X sucks because you can write bad code in X" argument, which is not really that interesting.
I would say OOP for OOP's sake became a lot more common with the increase in popularity of Java. The euphoria of automatic memory management vanished with the increasing complexity of design patterns.
In most of the cases they are applied for any scenario that even slightly resembles a design pattern, with most of the energy expended in fitting the scenario into a pattern than to find the correct solution.
In most of the cases they are applied for any scenario that even slightly resembles a design pattern, with most of the energy expended in fitting the scenario into a pattern than to find the correct solution.
For most people OOP is basically synonymous with earlier C++ and now Java.
Now wonder people are frustrated, modifying a large project or doing any thing trivial requires the help of a IDE loaded with Intellisense goodies to help you navigate through levels and levels of abstraction, often you will have no clue what each layer does. This is good for reading but bad both for writing and maintaining code. I have often had to end scratching my head figuring out what a certain middle layer of code does. I am sure most people are generally lost figuring out the layers below AbstractClassFactoryFactoryFactoryFactory.java and can't really understand why there is method Something.SomethingElse.AbsoulutelySomethingElse.WhereAreWe.finallyWeMadeit() like that.
OOP in principle in itself is good but unfortunately the languages that it gets expressed really makes it case very weak. Every time I see a Java application with 90% of the code being try/catch statements, get/set methods and XML DSL's - I cringe. Every time I have to spend 30 mins trying to figure how to configure and use a logger I get totally lost.
When I think of OO the worst parts flash in front of my eyes.
Now wonder people are frustrated, modifying a large project or doing any thing trivial requires the help of a IDE loaded with Intellisense goodies to help you navigate through levels and levels of abstraction, often you will have no clue what each layer does. This is good for reading but bad both for writing and maintaining code. I have often had to end scratching my head figuring out what a certain middle layer of code does. I am sure most people are generally lost figuring out the layers below AbstractClassFactoryFactoryFactoryFactory.java and can't really understand why there is method Something.SomethingElse.AbsoulutelySomethingElse.WhereAreWe.finallyWeMadeit() like that.
OOP in principle in itself is good but unfortunately the languages that it gets expressed really makes it case very weak. Every time I see a Java application with 90% of the code being try/catch statements, get/set methods and XML DSL's - I cringe. Every time I have to spend 30 mins trying to figure how to configure and use a logger I get totally lost.
When I think of OO the worst parts flash in front of my eyes.
I had a very similar revelation to the OP in the article. Coincidentally, I also happened to be writing semantic analysis for a compiler using the visitor pattern.
From this I can conclude two things:
- It's definitely the wrong choice to do abstract syntax tree traversal using the visitor pattern. But what would be a better choice? When using Java?
- Writing a compiler can be a life changing experience.
These days I call myself a recovering object oriented programmer.
From this I can conclude two things:
- It's definitely the wrong choice to do abstract syntax tree traversal using the visitor pattern. But what would be a better choice? When using Java?
- Writing a compiler can be a life changing experience.
These days I call myself a recovering object oriented programmer.
A couple of excellent reads for arguments against OOP:
http://c2.com/cgi/wiki?ArgumentsAgainstOop
http://c2.com/cgi/wiki?ObjectOrientationIsaHoax
Personally, I'm on the fence, but I think as is the case with most tools, there is a right and wrong place to use them. The trick is figuring out when that is.
http://c2.com/cgi/wiki?ArgumentsAgainstOop
http://c2.com/cgi/wiki?ObjectOrientationIsaHoax
Personally, I'm on the fence, but I think as is the case with most tools, there is a right and wrong place to use them. The trick is figuring out when that is.
What I really want is to write functional code in a way that looks like OOP. OOP is so simple and straight forward in comparison, it just results in code that is not scalable or easily maintained.
I do think that FP IS a snake oil fad and unsuitable for 90% of all code, which is all about mutable state and IO: manipulating data bases and game worlds, writing and reading files, sending and receiving network packages, user interaction etc. all of which is awkward in FP.
Back then OOP was paraded around as the silver bullet of software engineering some people rightfully pointed out that many problems do not map cleanly to OO abstractions. People tried to force all code into that straightjacket anyway, most infamously Java, and thus we ended up with things like "Singleton"s and "AbstractFactoryFactory"s.
Yet OOP was a success, because most problems can be expressed in a decent to good way using that paradigm, and it forced some structure on bad programmers. In contrast, most problems can not be expressed in a straightforward way in FP and the advantage of forcing structure on bad programmers is mostly gone.
I think the current FP fad is actually an unfortunate side effect (pun intended) of the OOP craze. All these younger programmers who learned programming with Java, who had to write all that AbstractFactoryFactory "Enterprise Java" code, they see FP examples and think they have seen the light.
As the article almost admits, FP is basically a restricted subset of procedural programming. However, while these people were taught that "procedural programming" is an archaic evil and everything must be wrapped into classes by their Java schools, nobody told them that about FP (because the instructors probably did not even consider/know it). So now they can enjoy the simplicity and freedom of procedural code without the feeling of being "politically incorrect".
However, as I said, FP is simply unsuitable for most real world problems. The problematic thing about Haskell is not understanding monads, it's that they are necessary! They and other constructs like that are the "Singleton"s and "AbstractFactoryFactory"s of FP - what happens if you try to force something naturally "unfunctional" into a functional straightjacket.
The right solution is multi-paradigm programming. Use FP where it makes sense, don't use it where it doesn't.
Back then OOP was paraded around as the silver bullet of software engineering some people rightfully pointed out that many problems do not map cleanly to OO abstractions. People tried to force all code into that straightjacket anyway, most infamously Java, and thus we ended up with things like "Singleton"s and "AbstractFactoryFactory"s.
Yet OOP was a success, because most problems can be expressed in a decent to good way using that paradigm, and it forced some structure on bad programmers. In contrast, most problems can not be expressed in a straightforward way in FP and the advantage of forcing structure on bad programmers is mostly gone.
I think the current FP fad is actually an unfortunate side effect (pun intended) of the OOP craze. All these younger programmers who learned programming with Java, who had to write all that AbstractFactoryFactory "Enterprise Java" code, they see FP examples and think they have seen the light.
As the article almost admits, FP is basically a restricted subset of procedural programming. However, while these people were taught that "procedural programming" is an archaic evil and everything must be wrapped into classes by their Java schools, nobody told them that about FP (because the instructors probably did not even consider/know it). So now they can enjoy the simplicity and freedom of procedural code without the feeling of being "politically incorrect".
However, as I said, FP is simply unsuitable for most real world problems. The problematic thing about Haskell is not understanding monads, it's that they are necessary! They and other constructs like that are the "Singleton"s and "AbstractFactoryFactory"s of FP - what happens if you try to force something naturally "unfunctional" into a functional straightjacket.
The right solution is multi-paradigm programming. Use FP where it makes sense, don't use it where it doesn't.
"The problematic thing about Haskell is not understanding monads, it's that they are necessary! They [... are ...] what happens if you try to force something naturally "unfunctional" into a functional straightjacket."
Er, I'm going to have to disagree with that as strongly as it is possible to do so. Monads are an incredibly powerful abstraction, to the point where they'd be useful even in a side-effect-ful language; for instance, for expressing non-determinism, or exceptions, or continuations, or any number of other features that would bloat your language spec if they were built-in. In Haskell's case, one of those features is IO, yes; but they're also used to provide many other features, including all the ones I just mentioned.
And every feature implemented via monad is restricted to only the parts of the code where you need it - your exceptions aren't going to sneak out of the module they're meant to be in because of a forgotten catch; you can parallelise non-IO code with impunity, knowing there are no surprise side-effects; etc etc.
I'd be inclined to compare monads, in terms of utility, to the abstract notion of objects, or to LISP-style macros.
"Use FP where it makes sense, don't use it where it doesn't."
Okay:
http://augustss.blogspot.co.uk/2007/08/programming-in-c-ummm...
There. Reasonably clean imperative code where you want it, with Haskell's enforced purity stopping your side-effects leaking into parts of your code where you don't want them. If you'd started from an inherently imperative base language, that wouldn't be possible. And all thanks to monads.
[edit: a discussion of monads in the imperative context of C#: http://mikehadlow.blogspot.co.uk/2011/01/monads-in-c1-introd... ]
Er, I'm going to have to disagree with that as strongly as it is possible to do so. Monads are an incredibly powerful abstraction, to the point where they'd be useful even in a side-effect-ful language; for instance, for expressing non-determinism, or exceptions, or continuations, or any number of other features that would bloat your language spec if they were built-in. In Haskell's case, one of those features is IO, yes; but they're also used to provide many other features, including all the ones I just mentioned.
And every feature implemented via monad is restricted to only the parts of the code where you need it - your exceptions aren't going to sneak out of the module they're meant to be in because of a forgotten catch; you can parallelise non-IO code with impunity, knowing there are no surprise side-effects; etc etc.
I'd be inclined to compare monads, in terms of utility, to the abstract notion of objects, or to LISP-style macros.
"Use FP where it makes sense, don't use it where it doesn't."
Okay:
http://augustss.blogspot.co.uk/2007/08/programming-in-c-ummm...
There. Reasonably clean imperative code where you want it, with Haskell's enforced purity stopping your side-effects leaking into parts of your code where you don't want them. If you'd started from an inherently imperative base language, that wouldn't be possible. And all thanks to monads.
[edit: a discussion of monads in the imperative context of C#: http://mikehadlow.blogspot.co.uk/2011/01/monads-in-c1-introd... ]
Read "Tackling the Awkward Squad" paper by Peyton-Jones et al.
Link for the lazy:
http://research.microsoft.com/en-us/um/people/simonpj/papers...
It's a good bit of writing, that.
http://research.microsoft.com/en-us/um/people/simonpj/papers...
It's a good bit of writing, that.
This sounds like OOP gone horribly wrong. OOP isn't for the code's benefit; it's for _my_ benefit.
Some problems can be easily thought about in terms of independent objects; it makes it easy to work out the solution, and to code an elegant, coherent solution. Some problems just don't suit that kind of thinking. Applying OOP to something it's not suited for and then blaming OOP for the resultant mess is just silly.
Some problems can be easily thought about in terms of independent objects; it makes it easy to work out the solution, and to code an elegant, coherent solution. Some problems just don't suit that kind of thinking. Applying OOP to something it's not suited for and then blaming OOP for the resultant mess is just silly.
I prepared this whole rant about how displeased I am with your article and how it doesn't really say anything. Instead I want to recommend that you try Objective C.
You can do most of the string, dictionary, array things using immutable objects, which is nice, and the mutable things like windows are usually used to hold state like positions, colors, etc., and so it's easy to conceptualize what you are mutating:
NSRect frame = [window frame]; frame.origin.x += 5; [window setFrame:frame];
Singletons are rarely used, mostly for what is basically an event responder (called the application delegate), and objects that are basically a way to group functions (NSFileManager to do IO functions, NSNotificationCenter to send application-wide events).
They've even got a first-class function object.
I have no desire to use Java, and it seems you don't either, but I think you should consider other OOP languages. It's fine to prefer functional programming (I sure do) but if you don't get the visitor pattern, I cannot help but think you don't know enough about it to write OOP off entirely.
You can do most of the string, dictionary, array things using immutable objects, which is nice, and the mutable things like windows are usually used to hold state like positions, colors, etc., and so it's easy to conceptualize what you are mutating:
NSRect frame = [window frame]; frame.origin.x += 5; [window setFrame:frame];
Singletons are rarely used, mostly for what is basically an event responder (called the application delegate), and objects that are basically a way to group functions (NSFileManager to do IO functions, NSNotificationCenter to send application-wide events).
They've even got a first-class function object.
I have no desire to use Java, and it seems you don't either, but I think you should consider other OOP languages. It's fine to prefer functional programming (I sure do) but if you don't get the visitor pattern, I cannot help but think you don't know enough about it to write OOP off entirely.
I think part of what might be going on here is that OOP patterns incur a large upfront cost in complexity, but the hope is that it will payoff later when it comes to maintaining and extending the code. For example, the visitor pattern he talks about might seem needlessly complex. Why scatter the logic for a recursive tree traversal across several classes when I could do it here, all in this one function? The response, of course, is that once the program starts to grow, and you want to write other modules that traverse that same structure, you won't have to copy and paste that same logic everywhere. Simply write another class that conforms to your visitor's interface. So, this reduces code duplication, and decouples the operations your performing on the tree, from the way that you're traversing the tree.
Another issue is that static type systems often make OOP design patterns seem more complex than they really are. Compare, for example, Python's visitor (http://pythonwise.blogspot.com/2006/06/visitor-design-patter...) with Java's (https://en.wikipedia.org/wiki/Visitor_pattern#Java_example). Python simply uses reflection to pull out the right visit method. This is almost a one-liner. Java, on the other hand, needs a complex double-dispatch setup to please its type system. Now, I'm not saying that Python's approach is better. There's a trade off involved, and, in fact, it's the exact same trade off as before. Python's approach is simpler at first, but, once your program starts to grow, you might find that catching type errors at compile time is a very handy feature indeed.
Also note that functional programming isn't without its seemingly complex design patterns. Monads are the obvious example, but there are many more: http://www.haskell.org/haskellwiki/Category:Idioms
Another issue is that static type systems often make OOP design patterns seem more complex than they really are. Compare, for example, Python's visitor (http://pythonwise.blogspot.com/2006/06/visitor-design-patter...) with Java's (https://en.wikipedia.org/wiki/Visitor_pattern#Java_example). Python simply uses reflection to pull out the right visit method. This is almost a one-liner. Java, on the other hand, needs a complex double-dispatch setup to please its type system. Now, I'm not saying that Python's approach is better. There's a trade off involved, and, in fact, it's the exact same trade off as before. Python's approach is simpler at first, but, once your program starts to grow, you might find that catching type errors at compile time is a very handy feature indeed.
Also note that functional programming isn't without its seemingly complex design patterns. Monads are the obvious example, but there are many more: http://www.haskell.org/haskellwiki/Category:Idioms
Have you ever worked on a project with millions of lines of code which is supposed to work together as a single piece? That is where OO is required. OO works when you design your system to be composed of blackbox components. Right level of abstraction is the key. Facades must expose blackbox functionality to other components. You are never to look into the innards of a component (the whitebox model do not work). These days the WEB CRUD apps do not require OO as each service rarely exceeds few hundred lines and they never work as a monolithic piece. But once you put your head into a Telecom Switch, a flight control system, an MRI scanner - OO is the way to go.
I think these days exponents of so called 'Functional Programming' just oversee the importance of abstraction in OO and try to use it in CRUD apps where it is clearly not warranted and then hurl abuses. Get a Life!
I think these days exponents of so called 'Functional Programming' just oversee the importance of abstraction in OO and try to use it in CRUD apps where it is clearly not warranted and then hurl abuses. Get a Life!
Have you ever worked on a project with millions of lines of code which is supposed to work together as a single piece? That is where OO is required.
I think you are conflating OO with modular design. The latter does not imply the former.
I think you are conflating OO with modular design. The latter does not imply the former.
I did some work programming a sequence for a Siemens magnet, and while I can't comment on the quality of the code driving the hardware, the OOP API they exposed for sequence creation was as bad as it gets. There was no sense of abstraction at all; it was very much like someone took an old C-style library then systematically hid all the functions in different classes for fun. Now, I could imagine that someone somewhere declared the magnets would be cutting-edge C++, and that's exactly what happened.
The point is that OOP is not necessarily the obviously correct way to manage the complexity of an MRI sequence. Any other style could have easily been substituted and not done worse.
The point is that OOP is not necessarily the obviously correct way to manage the complexity of an MRI sequence. Any other style could have easily been substituted and not done worse.
The point is - OO was developed for managing complexity of large projects which were using procedural model at the time. Large teams found it easy to work in the OO paradigm as each component could be abstracted down to a facade they need to call. Obviously if you don't do it right - it is not going to work. But Functional Programming exposes exactly the same pitfalls of procedural model (it is just another name) which OO tried to address.
It seems to me that you're talking about modularization for managing complexity, which FP does just fine. OOP doesn't have a monopoly on that. It takes discipline, but you can write well-structured code in any paradigm.
To say that functional is the same as procedural programming is a complete misunderstanding of both. To say OO tried to solve the pitfalls of the other two paradigms is a misunderstanding of OO on the entire spectrum, from Alan Kay's original vision to modern implementations.
Even forgiving those confusions for a moment, if it takes discipline to prevent your OO code from degrading to procedural code, how is it any better from just enforcing discipline on procedural code? If OO is quantifiably better, the default state of the code should be obviously better than with other techniques.
To say that functional is the same as procedural programming is a complete misunderstanding of both. To say OO tried to solve the pitfalls of the other two paradigms is a misunderstanding of OO on the entire spectrum, from Alan Kay's original vision to modern implementations.
Even forgiving those confusions for a moment, if it takes discipline to prevent your OO code from degrading to procedural code, how is it any better from just enforcing discipline on procedural code? If OO is quantifiably better, the default state of the code should be obviously better than with other techniques.
Aren't some (many?) telecom switches run with Erlang? I haven't used it, but from what I understand it's more functional than OO, is it not?
Surely this criticism extends to any information hiding mechanism in programming? You could do exactly the same thing using closures.
I happy to talk about the flaws of OO programming, but I'm not convinced that allowing you to have encapsulated private state is one of them.
I happy to talk about the flaws of OO programming, but I'm not convinced that allowing you to have encapsulated private state is one of them.
If you write classes that have methods that can change a non observable state of the class then you have followed the wrong path. Not at all sure that functional programming will prove a cure though.
I love my share of anti oop rants and hoped for some functional praise.
Unfortunately I couldn't read the article on my mobile, because someone decided it would be a great idea to have floating social sharing gizmos covering the content. It's totally unreadable right now.
Unfortunately I couldn't read the article on my mobile, because someone decided it would be a great idea to have floating social sharing gizmos covering the content. It's totally unreadable right now.
After author graduates and writes some production code, please post a followup that shares the experience and says more than "college is harder than high school"
I see OO as a way of imposing model constraints in a problem domain... there may not necessarily be a need for objects to maintain state.
That can go both ways I guess. You can do OO right, you know?
Reading bad functional code can be just as painful as bad OO code -- trying to untangle a mess of fold's that an overzealous developer who just learned of catamorphisms wrote can be painful. Even more so if that young developer is you, five months ago.
Sure, the language doesn't impose as much discipline in Python/Java/… as in Haskell(+), so it might require more of you as a developer to write clear, readable code (note: not saying that it requires more of you as a developer in general, just that it's easier to fall in to lazy anti-patterns). But if you work in a decent code base most of the pitfalls in the post are basically straw men. A well designed OO system will generally not be riddled with singletons and arbitrarily mutable state.
(+) I'm using Haskell here cause that's the only purely functional language I have any experience with. Substitute as you please.