I recently came across a tweet stating that there were no people who “defend OO vs. FP” that actually used FP in real projects. This is not the first time I came across statements like this (like in a blog posts portraying functional programmers as being higher on the “evolutionary ladder” than object-oriented programmers). Another claim I hear again and again is that “only Functional Programming can solve the problems of the modern age (like multi-core computing)”. I disagree with both claims.
My first question when discussing this is always: What exactly do FP and OO mean? When I ask this question, I rarely get a satisfactory answer. To me this is no surprise, given that ideas from these two “worlds” are brought together in many languages: Functions as first-class citizens, higher-order functions and anonymous functions are concepts a lot of people associate with FP. However, they are also present in a lot of languages that are rather on the OO side of things. Meanwhile, the functional language Erlang has been called “the most object-oriented language of all” by one of its designers.
Putting a language in one of these two boxes has always been weird to me. I would argue that Haskell and Scheme have little in common. Are they more alike than Scheme and Ruby? But anyway – let’s discuss a few of the concepts I often hear described as being “functional”.
Once they grasp the concept of functions as first-class citizens, a lot of people want to use it in all programming languages. Ruby – an OO language in naïve classification – offers blocks for this purpose. Their usage is definitely considered idiomatic. Do you call it a “multi-paradigm” language instead of an OO now? Whom does that help?
Let’s talk about immutability. Not every FP language offers immutability (Scheme and Common Lisp, for example, don’t). Immutability in my opinion is easier to put in place when data and behavior are separated. And Immutability is a great idea. However, you can also use it in an OO language. Instead of mutating state, always return a new instance with the changed state. That’s a pattern you can try out in every OO language and see if you like it.
Pattern matching is another great idea. I can again see why it would be more common in an FP language. But in an OO language you can use polymorphism as an alternative. Even then: Nobody can stop you from bringing pattern matching into a new OO language.
Another interesting idea is to make side effects explicit, especially in the case of I/O with its high potential for errors. You can do this in any language. This is a question of design patterns and not necessarily category theory.
Is FP better than OO? I think this is the wrong question to ask. For me the question is: What concepts and ideas help me to write better code? And that answer might very well be “classes (no inheritance), SOLID principles, persistent data structures and immutability”.
Labeling ideas as either FP or OO does not help anyone. All it does is build gated communities which can’t exchange ideas. Condescending jokes about other programming communities are not helping anyone. All it does is to keep good ideas from spreading.
I love trying out new languages like Elm, Elixir or Idris. And every time I bring back good new ideas that I can apply in other languages I already know and question ideas I had before. And sometimes I add this language to my tool belt.
I think the question whether a language is OO or FP boils down to one thing: Does it separate behavior and data? And to be honest: This is a boring question for me. So instead of saying “FP is better” or “OO is better”, try to consider more concrete questions. Some suggestions:
- Do classes help to structure your code? Can namespaces offer the same benefit?
- Is inheritance helpful or harmful? What about roles / mixins / traits?
- Which problems does the actor model solve? Which ones are better solved by single-threaded concurrency? What about Communicating Sequential Processes?
- Do we need encapsulation if we have immutable state?
- Does immutability help me write safer multi-threaded code? If so, why?
- Which advantages do persistent data structures have? What are the disadvantages?
- Is my team more productive in a dynamic programming language or in a language with dependent types? Where do they produce more bugs? How do concepts like type inference, gradual typing, or clojure.specs change that?
Those are the kinds of questions that I find interesting. And I would love to discuss them whenever you run into me. For years, there were people fighting over Emacs and Vim. That didn’t help anyone. But when people stopped fighting and started looking at more fine-grained differences between the two editors, they realized that both have excellent ideas and combined them into a wonderful project called Spacemacs. I’m confident we can do the same with FP and OO.
Enforcing to make side effects explicit is a decision that has to be made at the language-design level. I don’t know an OO language that made this decision, but again I don’t see a reason why this should be impossible. ↩
We could also discuss if OOP is “only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things” that can be achieved in any language. But that will be a topic for another blog post. ↩