Why Do You Need to Know About Functional Programming

Why Do You Need to Know About Functional Programming

The programming paradigm that simplifies your code
Ferenc Almasi โ€ข Last updated 2021 November 11 โ€ข Read time 11 min read
Just like object-oriented programming, functional programming has its own concepts too. Learn how you can apply them to JavaScript to make your code better.
  • twitter
  • facebook
JavaScript

When I first started learning about functional programming, I had a hard time wrapping my head around it. I understood the concept and the main principles but I lacked the practical knowledge. With this tutorial, I want to cover not just the concepts, but give you examples and show you how you can apply the functional programming paradigm to your own code.

Letโ€™s first start by defining what is functional programming.

Functional programming is a programming paradigm.

Just like object-oriented programming, functional programming has its own concepts. For example, everything revolves around being pure โ€” functions always return the same output given the same input. They have no side effects, meaning they donโ€™t alter or mess with any data outside of their scope.

It also advocates being immutable โ€” once something is created, it cannot be changed. We may also often hear that functional programming uses a declarative approach as opposed to the imperative approach that is also used by the object-oriented paradigm.

These are just some of the concepts that make up functional programming. But why are these principles important? What can they give us?


Why Functional Programming Can Benefit Us

Itโ€™s important to mention that functional programming is not a new paradigm. In fact, Lisp which was developed in the late 1950s was heavily functional. Still, we can benefit from it today for a couple of reasons.

One of them is that it will make your code easier to reason about. It focuses more on the โ€œWhat is your program doing?โ€ instead of โ€œHow does it do its thing?โ€ โ€” meaning you go with a declarative approach opposed to imperative implementations. To demonstrate, take a look at the two examples below.

In the first example, you focus on how the program is doing its thing, while in the second, you focus on what the program is doing:

Imperative

Copied to clipboard!
for (let i = 0; i < products.length; i++) {
    products[i].price = Math.floor(product.price);
}
imperative.js

Declarative

Copied to clipboard! Playground
products.map(product => {
    product.price = Math.floor(product.price);

    return product;
});
declarative.js

The two implementations are doing the same thing; modifies an array so we have rounded numbers for each product. For this small example, it may seem like you are writing more code. But behind the scenes, map will also return you a brand new array, meaning your original products will be kept intact. This is immutability in action.

It also makes your code more easily testable as it focuses on small contained functions called pure functions. As mentioned before, these functions are deterministic. We can guarantee that if we keep passing it the same value, we get the same output.

In the end, functional programming makes your code easier to reason about. It makes it easier to read and follow the process you took and makes your application less prone to bugs. In case something still goes wrong, itโ€™s easier to troubleshoot since your code is more concise.

To demonstrate how you can use functional programming in action, Iโ€™ve prepared some code examples that show you how to be declarative.


Declaring What You Mean

One of the best ways to start is by looking at array functions. Higher-order array functions are a good example of the functional programming approach.

Array functions

I have an entire article describing some of the array methods mentioned here, which you can check in the link below:

The Power of Higher-Order Array Functions

but letโ€™s quickly go through some of the more important ones and see what they do and how they shorten our code to make it more readable.

Array.prototype.find
Used for finding a specific element that passes the test, returns the first match

Copied to clipboard!
// Even if we have multiple products that are on sale, it will only return the first match
products.find(product => product.onSale);
find.js

Array.prototype.filter
Used for returning the elements that pass the test, returns every match

Copied to clipboard!
// This will return every product that is on sale
products.filter(product => product.onSale);
filter.js

Array.prototype.every
If every element meets the criteria, it will return true

Copied to clipboard!
// Every product should have a name so we get back true
products.every(product => product.name);
every.js

Array.prototype.some
If at least one element matches the criteria, it will return true

Copied to clipboard!
// If we have at least one product that is on sale, we get back true.
products.some(product => product.onSale);
some.js

Array.prototype.map
Used for transforming an array, gives back a new one

Copied to clipboard! Playground
// Rounding prices for products
products.map(product => {
	product.price = Math.floor(product.price);

	return product;
});
map.js

Array.prototype.reduce
Used for producing a single value from an array

Copied to clipboard!
// Sum the prices of each product
products.reduce((accumulated, product) => accumulated + product.price, 0);
reduce.js

You can already see how these array methods can shorten our code instead of using for loops, but we can make them even more powerful by chaining them. Most of these functions return an array, on which we can call another method and keep going until we get the desired result.

Function chaining

Function chaining is another great concept. It makes your code more reusable and again, reduces the noise and creates a shorter, more concise code that is both more readable, and in case of any bugs, itโ€™s easier to debug.

In the example below, youโ€™ll see that since each function call returns an array, you can keep calling new functions on them to create a chain.

Copied to clipboard! Playground
const round = (num) => Math.floor(num);
const isDivisibleByTwo = (num) => num % 2 === 0;
const add = (accumulated, num) => accumulated + num;

const numbers = [0, 1.2, 2.4, 3.6, 4.8, 5, 6.2, 7.4, 8.6, 9.8];

const sum = numbers.map(round)
                   .filter(isDivisibleByTwo)
                   .reduce(add, 0);
functionChaining.js

Instead of using three different for loops to get the desired value, we can simply call functions one after another and get it done in 3 lines.

Last but not least, libraries can help you avoid writing down the same things over and over again โ€” and reinventing the wheel โ€” by introducing helper functions for commonly occurring problems.

Libraries

There are many libraries out there that are following the functional programming paradigm. Some of the more well known are Lodash and Ramda. To give you some visual differences between the two letโ€™s take a look at how you can retrieve nested properties in each โ€” a commonly occurring problem. If one of the objects does not exist, we will get an error saying:

Getting cannot read property error in Chrome

Letโ€™s say we have a user object where we want to get their email address:

Copied to clipboard! Playground
const user = {
    name: 'John Doe',
    dob: '1999.01.01',
    settings: {
        email: 'john@doe.com'
    }
}
user.js

Lodash
Lodash uses underscore

Copied to clipboard!
// returns "john@doe.com" || undefined
_.get(user, 'settings.email');
lodash.js

Ramda
Ramda uses R

Copied to clipboard!
// returns "john@doe.com" || undefined
R.path(['settings', 'email'], user);
ramda.js

In each library, we can avoid getting an error if the parent of email does not exist. Instead it silently fails with an undefined.

Now you have a better understanding of how to be more declarative. What are some other important concepts in functional programming? โ€” Itโ€™s in the name, it is functions.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
Master JavaScriptinfo Remove ads

Functions

Functions are not only an essential part of functional programming but of JavaScript as well. They can help you break up your code to smaller, more digestible pieces. It increases readability and makes your code more easily testable by separating your code into smaller sections, often called components.

There are many concepts of how we can use functions to our own advantage. Letโ€™s see some of the more commonly occurring definitions you can find in functional programming.

Pure functions

As discussed previously, pure functions donโ€™t depend on any data other than what is passed into them. They also donโ€™t alter any data other than what they returned.

To give you a practical example for pure functions, think of the Math object:

Copied to clipboard! Playground
// This will return ??? - we don't know
Math.random();

// This will return 10, no matter what.
Math.max(10, 5);
pure.js

Here, Math.random is impure since it always returns a different value, even if we were to pass it the same input. Math.max however is a pure function since it will return the same output given the same input.

We need to note that in case our function doesnโ€™t have a return value, it is not pure.

First-class functions

In JavaScript and other functional languages, functions can also be assigned to variables and you can pass them around, just like they were variables.

Copied to clipboard! Playground
const greet = function () {
    console.log('๐Ÿ‘‹');
}

// The greet variable is now a function, we can invoke it
greet();
firstClassFunction.js

Higher-order functions

A higher-order function is nothing more than a simple function that takes in another function as one of its arguments. Functions that return another function are also called higher-order functions.

A great example for higher-order functions are previously discussed array functions such as filter or map.

Function composition

Function composition is all about combining functions to form brand new functions.

For example, Ramda has the compose function which takes in a list of functions as arguments and returns a function. You can call this with the input for which you want to apply the series of functions.

Copied to clipboard! Playground
// Produces 7.283185307179586
R.compose(
  R.add(1), 
  R.multiply(2)
)(Math.PI);
Ramda.js

Currying

Currying is a technique where you call a sequence of functions with one argument instead of calling one function with multiple arguments. Each function returns another function. The function at the end of the chain returns the actual expected value.

Copied to clipboard! Playground
// Instead of
const add = (a, b, c) => a + b + c;

add(2, 2, 2);

// Currying does
const curry = (a) => {
    return (b) => {
        return (c) => {
            return a + b + c;
        }
    }
};

curry(2)(2)(2);
currying.js

Recursion

Recursion happens when a function keeps calling itself until some condition is met. In the example below, we are counting down from 100.

Copied to clipboard! Playground
finalCountdown = (number) => {
    // If we don't specify an exit criteria, the number will continue into minus until the browser crashes
    if (!number) {
      return;
    }

    console.log(`It's the final countdown! - ${number}`);

    finalCountdown(number - 1);
}

// Will print out numbers from 100 till 1
finalCountdown(100);
recursion.js

Itโ€™s important to specify an exit condition otherwise you will create an infinite loop that eventually crashes the browser.

Now if you feel like you are starting to become overwhelmed by the amount of information, donโ€™t worry, itโ€™s a good sign that means you are expanding your knowledge. There are only two more important concepts we need to cover. They go hand in hand. They are immutability and side effects.


Immutability

When we talk about immutable variables and objects, we simply mean that once declared, their value canโ€™t be changed. This can reduce the complexity of your code and make your implementation less prone to errors.

To demonstrate immutability through an example, letโ€™s say you have an array where you need to remove the first item. Take a look at the differences below:

Copied to clipboard! Playground
const presents = ['๐ŸŽ', '๐Ÿ“ฆ', '๐ŸŽ€', '๐Ÿ’', '๐ŸŽ„'];

// --- Mutable solution ---

// we get back ๐ŸŽ
// and presents will be equal to ['๐Ÿ“ฆ', '๐ŸŽ€', '๐Ÿ’', '๐ŸŽ„'];
presents.shift();

// --- Immutable solution ---

// newPresents will be equal to ๐Ÿ“ฆ ๐ŸŽ€ ๐Ÿ’ ๐ŸŽ„
// and presents will be still equal to ['๐ŸŽ', '๐Ÿ“ฆ', '๐ŸŽ€', '๐Ÿ’', '๐ŸŽ„'];
const newPresents = presents.slice(1);
immutability.js

In the first example, we modify the original array with the shift function. If we want to achieve the same but keep the original array intact, we can use slice instead. This way you can avoid having unforeseen bugs in your application where you unintentionally modify data that should be kept in pristine condition.

One downside of immutability is performance. If you create too many copies you will run into memory issues so in case you operate on a large data set, you need to think about performance.


Side Effects

We also need to talk about side effects not because they are part of the functional programming paradigm but because they happen regardless of what programming pattern you take. They are an important part of any program and you need to know when and why they happen.

So what are side effects? โ€” Side effects can occur when a function is impure, therefore it does not necessarily return the same output given the same input. One commonly occurring example would be a network request. No matter what is the input, you can get back anything from 200 (OK) to 500 (Internal Server Error).

So you canโ€™t avoid having side effects and your goal shouldnโ€™t be to eliminate them entirely, but rather to be deliberate. Deliberate about why and when they happen.


Summary

Functional programming is a great way to organize your code in a better way. There are other programming paradigms out there like object-oriented programming. So what should you use, which is better?

Thereโ€™s really no answer, it depends on your situation and thereโ€™s no one above the other. You can also combine multiple paradigms together so itโ€™s not a โ€œone way or the otherโ€.

Thank you for taking the time to read this article, happy coding!

  • twitter
  • facebook
JavaScript
Did you find this page helpful?
๐Ÿ“š More Webtips
Mentoring

Rocket Launch Your Career

Speed up your learning progress with our mentorship program. Join as a mentee to unlock the full potential of Webtips and get a personalized learning experience by experts to master the following frontend technologies:

Courses

Recommended

This site uses cookies We use cookies to understand visitors and create a better experience for you. By clicking on "Accept", you accept its use. To find out more, please see our privacy policy.