A beginner's guide to Functional Programming in Javascript

A beginner's guide to Functional Programming in Javascript

Functional programming is a programming paradigm where you write code in a declarative approach using pure functions. Functional programming avoids mutating data and side effects.

Let's cover some of the important concepts about functional programming

Functions in Javascript are first class citizens

Functions are first-class citizens in Javascript. This means you can treat functions like any other variables. This means you can pass a function to other functions as an argument, you can return a function from another function and you can also assign the function as a value to a variable.

Let's see an example to understand this better

const greeting = (name) => `Hello, I am ${name}`;

console.log(greeting('John'));  // Hello, I am John

In the above example, I am assigning an anonymous function to a variable. This variable can be used anywhere later to invoke the function.

You can even add functions to objects and arrays.

const person = {
  name: 'John',
  greeting: () => 'Hello'
};

const arr = ['red', 'green', 'blue', () => 'Hello World'];

Pure Functions

One of the most essential concepts in functional programming is pure functions. This makes our functions predictable and reusable. If you don't know what pure functions are you can read it from my other blog Pure Functions in Javascript.

A quick TL;DR

  • Pure functions are simple reusable functions that take at least one argument, process it, and give an output (given the same input will always return the same output).
  • Pure function should not mutate any of its arguments.
  • Pure functions should always return a value or other function.
  • Pure functions should not perform any side effects.

Immutability

Immutability is an important concept in functional programming. A mutable object is the one that can be modified after its creation. Immutable objects are the ones that cannot be modified after their creation.

So how do we create an immutable object?

We can simply use the Spread syntax (...)

Suppose we have a person object where we want to increase the person's age without mutating the original.

const person = {
  name: 'John',
  age: 32
};

const newPerson = {...person, age: person.age + 2};

console.log(person);  // {name: 'John', age: 32}

console.log(newPerson);  // {name: 'John', age: 35}

Here, I am using the spread operator to copy all the key value pairs from the person object to the newPerson object and overwrite the age property. You can also add new values in the same operation.

const newPerson = {...person, age: person.age + 2, lastName: 'Doe'};

console.log(newPerson);  // {name: 'John', age: 34, lastName: 'Doe'}

But what about arrays? How do we make arrays immutable?

If you want to add a value to an array you can use the same Spread syntax shown above.

const colors = ['red', 'yellow', 'blue'];

const newColors = [...colors, 'green', 'orange'];

console.log(newColors);  // ['red', 'yellow', 'blue', 'green', 'orange']

But what if you want to change the values without mutating the original array?

Arrays in javascript have inbuild methods that can be used to maintain immutability.

Let us see what some of these methods are in brief in the next topic.

Higher Order Functions

A Higher Order function is a function that is passed to another function as an argument or is returned from a function.

const hello = () => 'Hello';

const greeting = (func, name) => `${func()}, my name is ${name}`;

console.log(greeting(hello, 'John'))  // Hello, my name is John

In the example above, the greeting function accepts the hello function as an argument and invokes it later.

Let's see some of the most used array methods which use higher order function to maintain immutability.

Array.map()

The Array.map() method iterates over each item in an array and modifies it using a callback function. Let's see with an example how the map method works. Say we have a number array and we want to double it without mutating the original.

const nums = [1, 2, 3, 4, 5];

const doubles = nums.map((num) => num * 2);

console.log(nums);  // [1, 2, 3, 4, 5]

console.log(doubles);  // [2, 4, 6, 8, 10]

See how declarative the syntax is? We just tell the map method we need to double each value. That's it! Array.map() takes care of iterating over each element, running it through the function, and then returning a brand new array with all the results. You can read more about it here.

Array.filter()

Array.filter() is another array method that iterates over an array, and filters out array items based on the condition passed in a callback function. The filter method also returns a new array, without mutating the original.

const nums = [2, 34, 9, 27, 8];

const lessThanTen = nums.filter((num) => num < 10);

console.log(lessThanTen);  // [2, 9, 8]

Again, as you can see in the above example, the array methods are quite declarative. You just tell the method you want to filter the array based on so and so condition and let it take care of filtering out the necessary values for you in a new array. You can read more about it here.

Array.reduce()

Array.reduce() method is a bit tricky, but once you understand it, it will be one of the most used methods in functional programming. The reduce method reduces the array to one single output. It accepts a callback function known as a reducer function and an optional second parameter of the initial value.

Let's see how to add all items in an array.

const nums = [2, 3, 8, 15];

Now let's write the reducer callback function to be passed into the reduce method. This function accepts two parameters known as the accumulator and the currentValue

const reducer = (accumulator, currentValue) => accumulator + currentValue;

Now let's use the reduce method.

const nums = [2, 3, 8, 15];

const reducer = (accumulator, currentValue) => accumulator + currentValue;

// Here I am passing second argument 0 as the initial accumulator value
const sum = nums.reduce(reducer, 0);

console.log(sum);  // 28

The reduce method iterates over the array and for each number in the array the reducer function runs. For the first iteration, the currentValue would be "2" and the accumulator "0". This adds up to "2". So on the second iteration the currentValue would be "3" and the accumulator would be "2". The loop will run till the end and we get our result. You can read more about the reduce method here.

Currying

Suppose we have a function add which accepts two parameters.

const add = (num1, num2) => num1 + num2;

Suppose for now we only have num1, and the num2 to be passed is not yet available. Now, what will happen if only num1 is passed to the function?

console.log(add(2))  // NaN

Since we only pass a single argument here, the second argument is undefined and the addition of these two arguments returns NaN.

This is where currying comes in. Since global variables are avoided in functional programming, we can pass an argument to add and return a function which in turn accepts the second argument and gives the expected output.

const add = (num1) => (num2) => num1 + num2;

// If the above syntax is confusing, you can think of it like this
const add = (num1) => {
  return (num2) => {
    return num1 + num2
  }
}

Here we can pass a value to the add function, which will save that value and return another function. You can then directly call the function or save it in a variable and reuse it later.


const result = add(2)(8)

console.log(result)  // 10

const plusTwo = add(2);

console.log(plusTwo(3));  // 5

console.log(plusTwo(4));  // 6

You can even make reusable new functions from the original to be reused later.

const plusThree = add(3);

console.log(plusThree(5));  // 8

console.log(plusThree(7));  // 10

Function Composition

In simple terms, function composition is passing a returned value of one function to another function as an argument. Let's see with an example what it looks like.

const sum = (num1, num2) => num1 + num2;

const squared = (num) => num * num;

console.log(squared(sum(2, 3)));  // 25

In the example above, the returned value of the sum(which will be 5) is passed as an argument to the squared function.

I don't know if you have noticed, but we have been using composition since the beginning of the blog. Yes!! The console.log(). Every time you call a function into console.log(myFunc()) you are just passing the returned value of myFunc to the console.log. This is what composition is.

Conclusion

  • Functional programming is a programming paradigm, where one codes in a declarative approach using pure functions.
  • Functional programming includes immutability, pure functions, higher order functions, currying, and composition.

Sorry for the long post. Here's a potato

Thank you for reading till the end 😄Let me know in the comments if you like the blog or if you have any suggestions!