What Is It?
The “Clash of Styles” series is a collection of six articles that provides a practical and unbiased comparison between Functional and Object-Oriented Paradigms on a fundamental level.
One of my main goals is to demonstrate that the core distinctions between Functional and Object-Oriented programming are not related to specific programming languages or tech stack. They have a far more practical and outreaching effect than most of us realize.
You are most probably using some of the general-purpose programming languages like Java, C#, Javascript, Python, etc. If so, you already have the tools in your hands to switch between FP and OOP on a daily basis.
Gone are the days when we could only use OO idioms because this is what our languages supported. For example, C# developers know that most of the language evolution in the last years was precisely related to adding more and more Functional constructs.
This is not a coincidence. We, as a community, we have acknowledged that there isn’t a silver bullet when it comes to program design. OOP has its’ strengths and weaknesses. The same statement holds for FP. It’s now our responsibility to decide what programming paradigm would fit better in what use case.
This is, however, not a trivial task. It requires continuous and dedicated efforts. You need to gain experience and train your intuition to recognize what programming style fits better certain types of problems.
That’s the primary purpose of the “Clash of Styles” series. I am confident that, after you go through the articles, you will understand the relations between OOP and FP and their diametrical natures.
What Is It Not?
This set of articles is not strictly about comparing specific programming languages or constructs. You will not see a lot of low-level, syntax-specific material.
Of course, as part of the implementation, I’ll be using real examples with actual implementations. My languages of choice are C# and F# because of my familiarity with them. Still, none of the discussions would be valid only for a concrete language.
Saying that, if you are interested in seeing some thorough comparison between Java and Scala, for example, I’m afraid you should look somewhere else.
Who Is It For?
I believe the reader of these articles can be any software developer with a couple of years of experience in the field.
The material is not advanced, but on the other hand, if you have just learned how to write a for loop, you may not be able to absorb most of the critical points.
I assume that you’re comfortable with some basic programming concepts, especially the ones from the OOP world – inheritance, polymorphism, encapsulation. Some basic knowledge of SOLID and Design Patterns may also be beneficial, but it’s certainly not mandatory.
If you are a senior developer with decades of experience in the field, you will probably be familiar with most of the material. However, these articles will help you discover some new and useful ideas. I’m confident you will gain some fresh perspective in areas you weren’t paying too much attention to.
The Practical Problem
All of the articles are built around a quite famous problem called the Expression Problem.
In a nutshell, I will be implementing a small interpreter for arithmetic expressions. This happens to be quite a good example problem when we want to compare the strengths and weaknesses of different programming paradigms.
I’ll be building every single feature both in OOP and FP with a lot of discussions on the pros and cons of the two styles in the specific scenario.
Languages Used
As I’ve mentioned already, the presented material is meant to be language agnostic. However, the examples are written in C# and F#.
Still, every piece of code you see will be very easily convertible to other statically typed languages like Java and Scala. Additionally, the core language constructs that I’ll use have their relatives in most of the Object-Oriented or Functional languages.
Therefore, you should be able to grasp the essence even if your preferred programming languages are quite different.
The Articles
Here is a reference to each of the articles with a brief description of the material it covers.
Part #1 – Operations Matrix via OOP
In Part #1, you will see how and why we can represent any requirements as a simple 2-D grid where the rows are the different types(variants), and the columns are the various operations supported on those types. I call this grid the Operations Matrix. It doesn’t matter what style of programming you prefer, implementing the requirements is all about filling out the cells of the Operations Matrix. In Part #1, I’ll do that using Object-Oriented Programming.
Part #2 – Operations Matrix via FP
In Part #2, I’ll show you how to “interpret” the requirements with a Functional Programming mindset. How does FP encourage us to decompose our program in terms of Operations? How is that precisely the opposite of the OOP perspective that you’ve seen in Part #1?
Part #3 – Extensibility via OOP and FP
Part #3 is one of the punchlines of the whole discussion. After you’re armed with enough understanding of how OOP and FP take the opposite perspective on the same problem, here you will see how choosing between the two can significantly affect the extensibility characteristics of your program.
What is “easy” to add in OOP is “hard” in FP and vice versa. How can that be presented in terms of the Open-Closed Principle?
Part #4 – Adding Support for Rational Numbers with FP
After Part #3, where you’ll see how the choice between the two styles can affect your program design quite significantly, in Part #4, you will explore the idea that the FP vs. OOP choice is not binary. Quite the contrary, you can, and you should mix the two styles. Whatever paradigm you select to be central to your design, there will be cases when the other style fits better. You need to be aware of that and make sure to use the right tools that fit your use case.
Part #5 – Double Dispatch, or When to Abandon OOP
In Part #5, you will learn one fairly complicated OOP pattern(trick), called Double Dispatch. You will see an example for which Double Dispatch is the only way to come up with a pure OOP solution. However, the key takeaway will be how much simpler and straightforward a Functional solution can be. This will come as another showcase that there isn’t a universally “correct” style of programming. The most sustainable solution always depends on the problem at hand.
Part #6 – FP in OOP via the Visitor Pattern
In Part #6, you will dig deeper into another well established OOP Design Pattern called the Visitor. Although you may be already familiar with the pattern, here you’ll look beyond the standard UML diagram and see how the Visitor addresses a fundamental limitation of Object-Oriented modeling. You will recognize how this pattern fills some of the gaps between OOP and FP.
Related Posts
Here is a list of posts that I don’t consider part of the series but are very much related to some of the topics.
Polymorphism on Steroids – Dive Into Multiple Dispatch (Multimethods)
The common understanding of Polymorphism takes into account only the runtime type of the “receiving” object. That’s the implementation in most languages. But what if we also consider the method arguments as part of the runtime method resolution logic? That’s the idea behind “Multiple Dispatch.”
This article explores the topic in depth. Also, it takes another look at the Visitor Pattern and Double Dispatch as ways to emulate Multiple Dispatch.
The “Programming Languages” Course in Coursera
The “Clash of Styles” series is very much influenced by some of the core ideas presented in the excellent three-part course “Programming Languages” on Coursera. I encourage you to read my review and pick up the course yourself.