Having beer with OOP and FP
Another article on Object Oriented vs Functional Programming
A few days ago, having a beer with some of my fellow developers the discussion about Object Oriented vs Functional programming raised once again. And I still notice, specially among less experienced developers some misunderstandings and a lot of confusion going on around this topic.
This article tries, to the best of my knowledge, to bring a clear and simple overview about these two programming paradigms with a practical example.
Functional Programming
There are numerous definitions on what functional programming is, to keep it as simple as possible lets define it like:
Functional programming is the process of building software by composing mathematical functions.
A few principles lay the foundations upon which this way of thinking thrives among so many developers and is being widely adopted in recent years.
- declarative language
- functions must be pure
- no shared state
- no side-effects
- immutable data
Let’s draw our story so that we can tackle it with both approaches and in the end compare them.
The final season of Game of Thrones is here, no spoilers don’t worry, this is the final war, battles will be fought and in the end the best warriors should be acknowledged by their bravery.
We will create a battle generator and in each time a battle is fought we will get the top 10 deadliest warriors.
Let’s start with a functional approach, go through the code below and after that we will start to break it down.
Declarative
“Imperative programming is like how you do something, and declarative programming is more like what you do.”
“Many (if not all) declarative approaches have some sort of underlying imperative abstraction.”
The declarative form is well expressed in the code above, as you loop through the functions declarations and calls you notice that they tell you more on what to do instead of how to do it.
Notice that there is kind of an imperative abstraction layer that hides some imperative implementation.
Take by example the function getTop10, they tell you that we go through all warriors, count their kills, sort them and take the first ten at a very high level without being very specific on how to do this. Later on we will check the same block in an imperative format.
Pure functions
A pure function is a function that given the same input will always return the same output and produces no side effects.
The functions getWarrior, getKills, getKillsByWarrior and getTop10 are all pure as they fit perfectly the above criteria.
getBattleResult is an impure function because it contains a random number generation inside, this means that if called twice with the same input it will (probably) return a different output.
printTop10 is another impure function, even though it satisfies the first criteria of pure functions it fails to do the same on the second one, logging to the console is a side effect since its observable outside the scope of the function.
(No) Shared state
Shared state is state which is shared between more than one function or more than one data-structure.
There is no shared state in our application since there is no global state and all functions receive the data they need by param and not accessing any outside data.
(No) Side Effects
Side-effect is any external effect a function has besides its return value.
Logging is a side effect in our application as we seen before, other than that our application has no side effects.
Immutability
Immutable object is one whose state cannot be modified after it is created
You might have already noticed but our code does not have a single let or var variable and its all composed by const variables (const variables sounds a bit confusing 😕).
Of course internally inside some functions (ex: reduce)there are values being changed but this happens at a lower level that what we are treating.
From our Program (Top Level) no variable is reassigned, mutated or altered in any manner and this is the line that matters here.
Object Oriented Programming
Take a look now at the OOP approach and in the end let’s compare the two and take some conclusions.
“… a programming paradigm based on the concept of “objects”, which can contain data, in the form of fields(often known as attributes), and code, in the form of procedures (often known as methods).” (in wikipedia)
The above citation gives us a good overview on the nature of OOP. Another relevant side from this approach is that it allows developers to reason about a program like they are reasoning about real life, being objects (classes) like real-life entities that have knowledge and can do work and live an independent existence.
Like the functional approach, object oriented also sets a few main principles, in this case there are 4:
- Encapsulation
- Data Abstraction
- Polymorphism
- Inheritance
For our practical implementation we will tackle again the same scenario used for the functional approach and after it will do the same analysis.
Encapsulation
Encapsulation is the inclusion within a program object of all the resources needed for the object to function — the methods and the data
Take for example the Battle class, it has all the data and logic that it needs to function independently, it knows how to manage itself. This behaviour leads to modularity which in large applications is crucial for maintainability.
Encapsulation prevents access to data except through the object’s functions*, this is important for preventing unintended changes to the instances of the class.
*While this is true for most languages in our case the concept of private does not exist natively in javascript, there are some hacks to mimic this behaviour and there is currently a stage 3 proposal to bring this feature to the language.
Data Abstraction
Abstraction means using simple things to represents complexity.
Abstraction and encapsulation are often confused, and the difference is subtle. Abstraction works at a design level while encapsulation works at an implementation level.
Let’s pick up again the class Battle, we know we can start a battle if we have an army.
We are not worried about how they fight, where they might be or the weapons they use. We don’t tell how it happens it just happens.
Polymorphism
Concept that objects of different types can be accessed through the same interface. Each type can provide its own, independent implementation of this interface.
One of the simplest examples of polymorphism is two classes implementing the same interface. The interface defines that the classes must have some method but leaves the implementation details to each one of them.
Javascript does not have the concept of interface, instead it uses duck typing.
“If it walks like a duck and it quacks like a duck, then it must be a duck”
But we can still get a practical example of this.
Honestly, I added the showInfo method because when I got to this part I hadn’t any clear example of polymorphism 😅. But this is a nice example both classes instances can call showInfo, on the person or on the warrior, and it works accordingly for each one.
Inheritance
A mechanism where you can to derive a class from another class for a hierarchy of classes that share a set of attributes and methods.
Extends is the keyword here, the class Warrior extends from Character, in this case Character is the parent class and Warrior is the child class. Both share a name property so only the parent needs to manage this property and it will be passed on to his children.
Looking closely the Warrior class implementation only refers to the name property inside the super method which means — I want to inherit the property name from the parent and not worry about that.
Comparison
Now that we have seen (and implemented) both approaches and their core principles it’s time to put them side by side and compare them.
While I wrote about how FP is written in a declarative format we can now see the contrast to OOP, which is more imperative, we write how to do, setWarriorsKills (OOP) is a good example, it describes the whole process to accomplish this task.
In OOP approach the army instance contains a collection of warrior instances, when adding a kill to a warrior the warrior changes and this change has a side-effect in the army because that warrior is a part of the warriors, there’s a shared state and instances are mutable unlike the FP approach where nothing changes and everything is created.
The OOP principles that we loop through don’t fit the FP approach, they are very tight to the OOP structure and it is hard to reason about them outside of that structure.
Both produce the same output and run the same scenario, but the OOP version ends up with about the double of lines of code comparing to the FP version. Normally the OOP approaches are more verbose than the FP versions, this example is no exception.
Testing was out of the scope of this article but looking at the code we can perceive that the FP version will be easier to test, much due to being composed by pure functions, while the OOP will be harder to test due to side effects and shared state.
Conclusion
It is not Black & White comparing these two paradigms, it is a lot of grey and many believe that the discussion doesn't even make sense. Probably you already use both on your daily work, even without realising it.
I believe that we should understand the problem/challenge first and then reason about the approach that makes more sense to tackle it.
They are not mutually exclusive, you can use both in the same codebase and get benefits from it.
Javascript was the language used in this article due to being very versatile which makes it easy to use the concepts of both paradigms.
We tend to associate this paradigms to specific languages but Programming languages are becoming evolved enough so that any attempt of categorisation is fuzzy and unpractical.
Regarding languages I encourage to check Scala, created by Martin Odersky. Object-Oriented Meets Functional was at the base of the design of Scala, and I think that most languages are evolving to get the best of both worlds, private properties in classes in Javascript and immutable data structures in Java are just a couple of examples. It is a great time to be a developer 😎.
OOP is more established while FP still looks like a new “trend”, even though it has been around for a few years.
Most of us, at school or at work or while learning online, were more exposed to the OOP paradigm and I believe that, sometimes, to think in FP is a mind shift that is not easy to make and takes time and openness.
Finally, keep in mind that this article provided a light overview about these two programming paradigms, there is a world more to learn about them. I hope it might help, in some way, when discussing this topic over a 🍺 with your colleagues.
Hope you have enjoyed the reading, if you have any suggestion, spotted a mistake or have a different opinion reach me in the comments, I am always eager to learn and hear different opinions.
By the way, what about your battles? 😄