When to Use Functional Programming vs OOP

Hana Belay - Dec 12 '22 - - Dev Community

Back in the old days, software projects were relatively simple in terms of scope and lines of code. However, through time, software projects have become drastically complex and the good old procedural programming paradigm no longer suffices. The more your software becomes complex, the more it is prone to bugs. Therefore, you need a way of managing and structuring your project in a way that is easy to debug, read, and scale.

A programming paradigm is a way or technique to solve a given problem using some tools and guidelines. A language may support multiple paradigms or just a single one. Most of the paradigms fall into either imperative or declarative.

The imperative style follows a series of computational steps by changing state. Programming paradigms that fall into this category include procedural programming (one of the oldest ones where instructions are grouped into procedures) and object-oriented programming which we will see later on.

The alternative declarative style focuses on what a program should do instead of the steps that it needs to follow. It does so without producing a change in state or a side effect. An example in this category is functional programming.

Today, we have object-oriented and functional programming at the forefront of programming paradigms. These relatively newer programming paradigms coupled with an improved software development process provide ways to cope with the complexity of code.

In this article, we are going to look at these two paradigms and compare them. Let’s get started!

Object Oriented Programming

A program in OOP is a group of objects that have an attribute and behavior.

  • Attribute - is the characteristics of a real-world object. For example, a student has an ID and a name. These attributes correspond to data that is held in variables.
  • Behavior - is the action an object performs in response to an event. For example, a student submits an assignment when told to do so by the teacher. These correspond to functions that are called to process some data and do something like displaying it on the console for instance.

The attribute and behavior together form a single unit called an object. Thus, in OOP, you think about how you can map real-world objects to objects in the programming sense. This could be just about anything; from physical objects to elements of a computer system.

A concept that is often associated with objects is class. Class is a group of similar objects. It is a blueprint that defines certain properties and behaviors for a group of related objects.

Here is a simple class diagram for a Student class:

Class Diagram for Student Class

There are four pillars of OOP. These are:

  • Inheritance is the concept of dividing classes into sub-classes. For example, a vehicle class can be divided into cars, buses, etc. In this approach, the child classes share common characteristics with the parent class while also adding their own features. The main advantage of this is the re-usability of code (DRY).
  • Abstraction is the concept of showing necessary information while hiding unnecessary implementation details. This decreases complexity and improves extensibility. Just like you don’t need to know how a car engine works to drive a car, you don’t need to know the meticulous details of how certain methods of a particular class work.
  • Encapsulation is the concept of restricting access to some data and methods of a class. You don’t need to access what you don’t need to access. This helps prevent accidental usage of data.
  • Polymorphism is the ability of methods to take on different forms. Polymorphism allows you to have a unique implementation of the same method. Concepts included in polymorphism are method overloading and method overriding.

The above concepts of OOP are highly dependent on one another. You can’t have polymorphism without inheritance for example. It is the relationship between objects through the different pillars that make up OOP.

Functional Programming

How many times have you scratched your head because the function you wrote didn’t behave as expected? Side effects of a function can introduce a lot of bugs into your code. This is where functional programming really shines. The foundational mathematical principle for functional programming is lambda calculus. Lambda calculus is an elegant representation of a function from a computational perspective. Expressions in lambda calculus are built using the following three things:

  • Variables
  • Building functions (lambda notation - λ)
  • and a way of applying functions

For example; a simple lambda expression for the increment operator may look like λx.x+1

In functional programming functions are pure. This means that functions take an input and produce an output without modifying the input.

For example, if you have a function getSqrt() that returns the square root of a number, given the same number (input), it should always return the same output for the square root.

In functional programming, you can look at a function and reason about it. This makes code readable and easier to test. Since functions in functional programming don’t have a side effect, you can run them side by side. They can’t interfere with one another. This makes functional programming very efficient for writing parallel and asynchronous programs.

Functions in functional programming are also first-class citizens. This means that they can be treated as primitives like integers and support all available operations; they can be stored in a variable, passed as an argument to another function, and returned as results.

Another concept in functional programming is higher-order functions and recursion. Instead of the traditional loop construct, functional languages take a declarative approach and use recursive functions and higher-order functions for iteration. A higher-order function (HOF) either takes a function as an argument or returns it. HOF is possible because functions are first-class citizens.

In functional programming, most of the data structures are immutable. Immutability is being unable to be changed. You cannot modify the value of a variable once it has been created. The only way to change it is to create a new one.

Although the functional programming paradigm is relatively less popular than OOP or other imperative programming paradigms, nowadays it is used in many real-world applications. Haskell, a purely functional programming language, is used in Facebook, for example, for spam filtering.

Comparison

OOP is imperative; it follows a clear set of computational steps to follow to get to the final result. Functional programming, on the other hand, is declarative; it describes the “what” i.e. what you want to achieve rather than the “how”.

The idea of executing commands in a sequence is pointless in functional programming because there is no change in state or a side effect.

Immutability together with the concept of pure functions makes functional programming easier to work with ****parallel and asynchronous programs.

Most languages nowadays have built-in support for both OOP and functional programming. For example, you can write a program in a functional programming paradigm using languages like Python or JavaScript. Functions like map and filter are higher-order functions. Thus, you can always have the best of both worlds. If you want to use a fully functional programming language, you have Haskell and many others such as Erlang.

Programming paradigms are different ways of solving a problem. Thus, if the problem can be solved by performing a set of computational steps on a growing number of entities, OOP is a good choice. On the other hand, functional programming is a good choice if you are going to have a growing number of operations on a fixed set of entities.

Functional programming shines when complexity is involved. As mentioned in the previous section, functions in functional programming are pure, immutable, and have no side effects. Thus, you can write code that has fewer bugs, more test coverage, and high scaling.

A program written in a functional programming paradigm uses a lot of memory because it always requires you to create new objects (immutability). A program written in the OOP paradigm can become less efficient as the complexity of the code grows because of the complex relationship between classes.

Functional programming is also used a lot in the frontend space of web development, so having an understanding of how things work can be really handy.

In OOP, you can have a smooth transition from real-world objects to objects in the programming sense whereas, in functional programming, the transition may not be as smooth.

At the end of the day though, it’s up to you and your team to assess your requirements, consider different variables like development time, community support, scalability, etc. and pick the paradigm that works best for your case.

Happy coding! 🖤


What’s your go-to paradigm? Let me know in the comments section below.

References

https://faculty.ksu.edu.sa/sites/default/files/ObjectOrientedProgramminginC4thEdition.pdf

https://doc.lagout.org/programmation/Functional Programming/Functional Programming For The Real World.pdf

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .