Reviewed by a tech expert

Functional programming in JavaScript

#Sales
#Sales
#Sales
#Sales
Read this articles in:
EN
PL

 

From this article you will learn how to write functional code in JavaScript. We will make use of the power of JavaScript functions and find out how easy it is to do complicated tasks with functional programming. At the end of the article you will be able to use functional aspects of JavaScript, implement memoization and operate functionally on your data. We will focus on some real world examples that will help you and your colleagues find ways to write better code.

Functional programming is a paradigm that encourages developers to write code that does not have mutable data and is stateless. By definition, it tells us to treat our programs as mathematical functions which always produce identical results when the same values are passed into them. Because we know what the output of the function will be, this approach reduces the amount of surprises in the code.

Table of content

  1. Declarative programming vs imperative programming
  2. Pure functions
  3. JavaScript and Functional Programming
  4. Const variables
  5. Avoid usage of loops
  6. Summary

Declarative programming vs imperative programming

In practice, you will probably need to use a mix of both. Functional programming is declarative. It means that we declare what action task to perform without specifying the exact steps to achieve that. Sounds a little like a magic, doesn’t it? To help you better understand what declarative programming is, we will use an example. Let’s say we have a list of the 100 richest people in the world, and want to get the total fortune of the top ten in the list. In imperative programming, we would probably do something like that:

const getTotalFortuneOfTenRichest = (richPeople) => {

richPeople.sort(

(first, second) => first.money - second.money

);



let sum = 0;



for (let i = 0; i < richPeople.length; i++) {

sum += richPeople[i].money;



if (i >= 10) {

return sum;

}

}

};

This function fulfills its role, but we can do better using declarative programming as an abstraction to our code. In imperative way of writing code we use specific instructions to modify structures, while in functional programming we specify what do we expect as a result.

  • sort function mutates the array. It is quite unexpected to pass a variable into a function and, after finishing the execution, notice that the variables have different values.
  • sum variable is mutable. It changes with every loop iteration. It is not so harmful now because it is only used in this function and its not leaving it. But the habit of making mutable variables may cause problems when we get used to it and start passing mutable variables across our application.
  • Another thing eliminated when following the guidelines of functional programming is declaring the execution flow. We specify what should be done in each step.
  • On top of that, there are conditions that could stop the loop iteration and return from function. Little things like that make the code harder to read and understand, as the tracking code execution involves different variables and evaluating conditions to track the flow.

Making the code functional in most cases means making it less complicated and more readable. It also allows us to follow other rules of writing good code.
Take a look at the same example, but implemented following the guidelines of functional style.

const getTotalFortuneOfTenRichest = (richPeople) => (

[...richPeople]

.sort((first, second) => first.money - second.money)

.slice(0, 10)

.reduce((total, person) => total + person.money, 0)

);

It is clearly more readable than the previous example. These declarative functions are most probably using imperative code under the hood, but with functional aspects of JavaScript in place we don’t have to bother with low level syntax to operate on structures.

Because we’ve defined the steps to be taken, the whole function is imperative, but composed of declarative blocks. We just have to specify what results do we expect after each operation. First, the richPeople array is now shallow copied using array spread operator to avoid modifying the input variable. Then, each step is declaratively described using functional aspects of JavaScript. We want the array to be sorted descending by people’s wealth. Next, we want only the first ten people. Finally, the last step is reducing the whole array to a single value, calculated by adding each person’s money to the total, starting from total equal 0.

Pure functions

Another important concept you might want to know is purity of the function. The best definition that springs to my mind is: if the function can be expressed as a pair of argument and its output, then it is considered pure. Pure functions always return the same result and depend only on arguments passed to them.

In practice, pure functions are really important for building applications – we can use them and compose with any other functions without having to worry about the result impacting the global state of the application. Pure functions give the confidence that there are no side effects that will modify some variables that are out of their scope.

Let’s take a look at an example to illustrate it. The previous function to calculate the sum of the fortune can be considered pure. Why? Because we can map the parameters to the function output:

No matter what, passing the same array to the function will result in the same output. But things are not always that way. Some function we write may seem pure, but they are not! Consider the following example:

const getFileContent = (fileName) =>

fs.readFileSync(fileName, 'utf8');

Is it function pure? It is not. At first glance, it returns file contents. Passing the same filename should always return the same output, right? But in reality, a can be moved around and deleted, and its contents may change. That means that even given the same filename, the function may return different content each time it is called, or throw an exception if the file is not found.

A pure function also should guarantee that it does not mutate the data and operates only within its own scope. The function in the first example is a perfect example of function with side effects. It this case, it only changes the order of elements in an array parameter, but what if the implementation removed the unwanted elements from the array? Then, we will get back modified array with incomplete data, which leads to serious bugs in the code which would be really hard to find.

JavaScript and Functional Programming

JavaScript encourages and provides possibilities to write functional code. Right now, we will take a look at functionalities that will allow us to write better applications.

Pair programming

Const variables

Since ECMAScript2015 you can use const and let to declare your variables. Using const when declaring your variable, you can prevent them from reassigning. For example, declaring sum as const sum would throw a TypeError telling us that we are trying to assign a value to a constant variable. Once you get used to writing pure functions, you will notice that there is no need to use let or var declarations. Instead, use constant variables everywhere as a standard to declare your variables.

Avoid usage of loops

JavaScript provides ways to get rid of loops. Declaring the flow of the application using loops means the code is imperative. If we want to write functional code we can use:

Map

The map function is useful for transforming elements of our array to the desired shape. Most probably, if you have ever stored people data in database, you have saved their birth date instead of their age. If you want to display their age, you can use the map function to do so.

const convertBirthdateToAge = ({ birthdate, ...person }) => (

{ ...person, age: getAgeFromBirthdate(birthdate) }

);



people.map(convertBirthdateToAge);

This executes provided function for every element in our array and returns a new array. The new array will contain the outputs of the convertBirthdateToAge function applied to the elements in the people array. The Map function takes a callback function which is applied for every element in an array and the result of that the callback function is appended to the result array. The callback function takes the currently processed element as the first argument and index of that element as the second argument. The index argument of callback can be used, for example, to add some identifier to the result array:

const addId = (element, index) => ({ ...element, id: index });



people

.map(convertToAge)

.map(addId);

The map function is also useful for executing many asynchronous operations at once without waiting for the result each time. Let’s stick with our people array. We want to save them to the database. Most probably, database operations are executed asynchronously and the first idea that might come to our mind is:

for (const person of people) {

await saveToDatabase(person)

}

The problem is we are waiting for each save operation to complete in each loop iteration. We can solve that problem using the map function.

const peopleSavers = people.map(saveToDatabase)

await Promise.all(peopleSavers)

In this example, we are mapping each person to the asynchronous function result which is a promise. Then we are waiting for all promises to complete.

Filter

The Filter function is useful for filtering an array in a declarative way. The signature of filter function is almost the same as of the map function. The only difference is that it takes a callback whose output indicates whether the value should be included in the result array or not. If the return value of callback is a falsy value, the currently processed element will not be present in the output array. As a result, we get an array of values fulfilling condition checked by a callback function.
As the output of both map function and filter function is array we can chain the calls and filter our array of people from previous example to contain only people that are at least 30 years old.

const isOlderThan30 = ({ age }) => age >= 30;



people

.map(convertToAge)

.filter(isOlderThan30);

Reduce

Reduce is another function that might look complicated at first glance but it is really useful for writing functional code. Reduce operates on array and executes the callback function for each element accumulating the value. In the end, the accumulated values are returned as a result. Let’s jump into an example to see the practical use of it. We have an array of working hours which looks like this:

const workingHours = [

{day: "MONDAY", startTime: 10, endTime: 15},

{day: "TUESDAY", startTime: 10, endTime: 12},

{day: "FRIDAY", startTime: 15, endTime: 21},

]

To convert the array into an object which contains days as keys, we can use the reduce function. In reduce, the first parameter is the callback function, and the second parameter is the initial value. The first parameter to the callback function is the accumulator – a value returned from the callback on the previous element. The second parameter is the currently processed element.

workingHours.reduce((

(previousValue, {day, ...hours}) => ({

...previousValue,

[day]: hours

})

), {})

The output should look like that:

{

MONDAY: { startTime: 10, endTime: 15 },

TUESDAY: { startTime: 10, endTime: 12 },

FRIDAY: { startTime: 15, endTime: 21 }

}

Currying in functions

Currying is an important part of functional programming. It allows to call functions without specifying all the required parameters. This means you will not get the result, but another function letting us pass the missing parameters. The result of the function is returned when all of the arguments are passed. Currying is essential for building reusable functions. For example, we can implement it in this way:

const getContentTemplate = content => (

user => (

`Hello ${user}

${content}`

)

);



const contentTemplate = getContentTemplate(`For your birthday we have gifted you 100 free points!`);

We can now reuse the function for different users. For a template filled with user greeting we have to execute:

const templateForMark = contentTemplate(‘Mark’);

Of course, we could also get the result instantly by calling:

getContentTemplate(template)(user);

Passing the template to the getContentTemplate function will only return another function that requires us to pass the user argument. Just after passing user string to the partial result, we will receive the final output like this:

Hello Mark

For your birthday we have gifted you 100 free points!

Higher order functions

In JavaScript, functions are first class values, which means you can pass them as arguments, return them, and operate on them like any other variables. You know how to pass them as callbacks in functional array methods. You can return functions (or objects with functions as values) as shown in the section about currying. This gives a lot of possibilities with combination of JavaScript closures. In the example below findInDatabase is a function that is passed to the repositoryFor function.

const repositoryFor = (entity, findInDatabase) => {

const MAX_RECORDS_LIMIT = 100;

return {

findAll: (filter) => (

findInDatabase(

entity,

{ ...filter, limit: MAX_RECORDS_LIMIT }

)

),

};

};

The object returned from the repositoryFor method has access to its closure and since it is enclosed in the repositoryFor method, it has access to the MAXRECORDSLIMIT variable.

With this knowledge, you can try to implement simple caching (known also as memoization) mechanism on your own. The cache could be stored in the repositoryFor method and all the methods could first check the cache, and return the cached value if found. Try to make use of functions as first class values and avoid duplication using function composition. Cached function could be decorated as cached(findInDatabase). I encourage you to find and implement more improvements to this code. Have fun using functional programming.

Summary

Declarative programming encourages writing clean and reusable code. It allows to compose blocks of declarative code to build modules that are highly testable, and contain easy to understand, readable code. Small composable functions also mean that we can follow the DRY rule.

I hope this article provided you with enough information to start exploring functional aspects in JavaScript and other languages. I believe you can find examples in your own code that can be improved using pure, immutable and side-effect free code.

If you are interested in fully functional code, check this library: https://ramdajs.com/.

Be a better programmer and go functional!

 

People also ask

No items found.
Want more posts from the author?
Read more

Want to read more?

Web

Post-deployment application performance monitoring

If you're a member of a development team (particularly a tester), this place is perfect for you. At a certain stage of the project, you publish the application.
No results found.
There are no results with this criteria. Try changing your search.
en