Unlocking the Power of Functional Programming in JavaScript: A Comprehensive Guide
Tags: webdev, javascript, programming, angular
Published: 2023-08-14 17:58:34

**Introduction:**

A functional programming(FP) is paradigm, means a way of thinking about software construction based on some principles like **Pure functions, Immutability, First class and higher-order functions, Function composition, Closure, Declarative Programming, Recursion, Referential Transparency, Currying and  Partial Application**

These principles, when applied effectively in JavaScript, can lead to code that is more modular, maintainable, resilient, more understandable, testable, and capable of elegantly handling complex problems.

This article looks quite long but not theoretical as such.

Let's start experimenting each item:

**1. Pure Functions:**

<u>Two Rules</u>

1. Given the same input, always return same output.
2. Produces no side effects

**Use:** Easy to refactor, makes code more flexible and adaptable.

**Example 1:**

```typescript
// Impure function.
let a = 4;
const multiplyNumbers = (b) => a *= b;

multiplyNumbers(3);
console.log(a); // first time: 12
> 12
multiplyNumbers(3);
console.log(a); // second time: 36
> 36

// Mutates external variable so it isn't pure.
```

```typescript
// Pure function.
const multiplyNumbers = (x,y) => x * y;

multiplyNumbers(2, 3);
> 6
```

**Example 2:**

```typescript
// Impure function.
addNumberarr = (arr, num) => {
arr.push(num);
};
const testArr = [1,2,3];
addNumberarr(testArr, 4);

console.log(testArr);
> [1, 2, 3, 4]

// Mutates input array so it isn't pure.
```

```typescript
// pure version of above.
addNumberarr = (arr, num) => {
return [...arr, num];
};
const testArr = [1,2,3];
addNumberarr(testArr, 4);
> [1, 2, 3, 4]
```
**JS Built-in Pure functions:**

```typescript
arr.reduce()
arr.map()
arr.filter()
arr.concat()
arr.slice()
arr.each()
arr.every()
... - spread syntax
```

**JS Built-in Impure functions:**

```typescript
arr.splice()
arr.push()
arr.sort()
Math.random()
```

**2. Immutability:**

Objects whose state can't be altered once it is created.

A simple example would be using the slice method to help you easily grasp the meaning.

```typescript
const arr = [1,2,3,4];

const slicedArray = arr.slice(1,2);

slicedArray
> [2]

arr
> [1, 2, 3, 4]
```
If you see above example, slice didn't alter or modify or mutate original array arr. Whereas, if you see below example:

```typescript
const arr = [1,2,3,4];

arr.push(5);
> 5

arr
> [1, 2, 3, 4, 5]
```
original array arr has been mutated. It's not that we shouldn't use push but we can avoid in most of the situations. Simple example would be:

```typescript
const arr = [1,2,3,4];

const newArr = [...arr, 5];

arr
> [1, 2, 3, 4]

newArr
> [1, 2, 3, 4, 5]
```
The above all are simple examples and won't create any issues possibly. But, situations where we keep on modifying same object wherever possible across whole file will create many issues. As we need to maintain the track as how many times and ways that object has been altered.

So, to solve this issue we need to avoid mutating the object.

**3. First Class functions**

First-class functions refer to the concept of treating functions as first-class citizens, which means they are treated as regular variables or values. It enable functions to be manipulated and used in the same ways as other data types like strings or numbers. This allows functions to be passed as arguments to other functions, returned as values from other functions, and assigned to variables. Javascript supports this.

It opens the door to powerful programming techniques, such as higher-order functions, function composition, and the creation of abstractions.

**4. Higher-Order Functions:**

A function can take a function as an argument or can return a function as a value is called a higher-order function.

1. A function that returns a function

```typescript
const higherOrderFunc = function() {
    return function() {
        return 12;
    }
}

// which returns below function hence it is higher order function.
higherOrderFunc(); 
> ƒ () {
        return 12;
    }

higherOrderFunc()();
> 12
```
2. A function that takes a function as an argument

```typescript
const testFunc = function(x) {
    return x + 12;
}

//which takes function as an argument.
const higherOrderFunc = function(testFunc) {
    return testFunc(8);
}

higherOrderFunc(testFunc);
> 20
```
**Example 1:**

```typescript
function calculate(operation, numbers) {
    return operation(numbers);
}

function addition(numbers) {
    let sum = 0;
    for (const number of numbers) {
        sum+=number;
    }
    return sum;
}

function multiply(numbers) {
    let sum = 1;
    for (const number of numbers) {
        sum*=number;
    }
    return sum;
}

const numbers = [1,2,3,4,5];
console.log(calculate(addition, numbers));
> 15

console.log(calculate(multiply, numbers));
> 120
```
_calculate(multiply, numbers) - Don't append parenthesis while sending function as an argument._

**Benefits of higher-order functions:**

Reduces code duplication
Single responsibility

In Javascript, functions can take arguments as primitives or objects and return the same called first-order functions.

**JS built-in higher order functions are:**

_arr.reduce(), arr.forEach(), arr.filter(), arr.map()_

**5. Functional Composition:**

It is an approach where result of one function passed on to next function.

```typescript
const add = (x, y) => x+y;

const subtract = (x) => x-4;

const multiply = (x) => x * 8;

// result of `add` is passed to `subtract` and its result passed to `multiply`.
const result = multiply(subtract(add(2, 3)));

result;
> 8
```
That looks readable but what if we have more functions to call one after other. Let's try little cleaner approach.

```typescript
const compose = (...functions) => x => functions.reduceRight((total, f) => f(total), x);

const add = x => x+2;

const subtract = x => x-1;

const multiply = x => x * 8;

compose(multiply, subtract, add)(2);
> 24
```
We can also use reduce to implement:

```typescript
const pipe = (...functions) => x => functions.reduce((total, f) => f(total), x);

const add = x => x+2;

const subtract = x => x-1;

const multiply = x => x * 8;

pipe(add, subtract, multiply)(2);
> 24
```
pipe - performs from left-to-right. 
compose - performs from right-to-left.

**6. Declarative Programming:**

**Declarative:** tells _What to do_

**Imperative:** tells _How to do_

**Example:** Find the employees with dept 'justCode' and summation of their salary.

**Imperative Style:**

```typescript
const employees = [
{id: 1, name: 'james', dept: 'admin', salary: 10000},
{id: 1, name: 'Tom', dept: 'finance', salary: 10000},
{id: 1, name: 'peter', dept: 'justCode', salary: 12500},
{id: 1, name: 'tunner', dept: 'justCode', salary: 14500},
];

const justCodeDept = [];

// filter employees based on dept name.
for (let i=0; i<employees.length; i++) {
  if (employees[i].dept === 'justCode') {
    justCodeDept.push(employees[i]);
  }
}

// summation of justCodeDept employees.
let summation = 0;
for (j = 0; j<justCodeDept.length; j++) {
  summation = summation + justCodeDept[j].salary;
}

console.log(summation);
```
**Declarative Style:**

```typescript
const employees = [
{id: 1, name: 'james', dept: 'admin', salary: 10000},
{id: 1, name: 'Tom', dept: 'finance', salary: 10000},
{id: 1, name: 'peter', dept: 'justCode', salary: 12500},
{id: 1, name: 'tunner', dept: 'justCode', salary: 14500},
];

console.log(employees.filter(item => item.dept === 'justCode').reduce(((previousValue, currentValue) => previousValue += currentValue.salary), 0));
```

**7. Currying:**
Splitting up function that takes multiple arguments into a sequence of functions that each take its individual argument (only one).

**Example 1:**

Generally, we write:

```typescript

function addition(x, y, z) {
    return x + y + z;
}

addition(1, 2, 3);
> 6
```

Currying:

```typescript
function addition(x) {
    return function addY(y) {
        return function addz(z) {
            return x+y+z;
        }
    }
}

addition(1)(2)(3);
> 6
```
Using arrow functions:

```typescript
addition = (x)=>(y)=>(z)=>x + y + z;

addition(1)(2)(3);
> 6
```

**Example 2:**

```typescript
function formWelcomNote(name) {
    name = `Hello ${name}, `;
    return function(location) {
        location = `Welcome to ${location},`;
        return function(section){
            return `${name}${location} Please visit ${section} section`
        }
    }
}

formWelcomNote('Yester')('VK Just Code Articles')('JS Articles');
> 'Hello Yester, Welcome to VK Just Code Articles, Please visit JS Articles section'
```
We can also write as:

```typescript
formWelcomNote = (name)=>{
    name = `Hello ${name}, `;
    return (location)=>{
        location = `Welcome to ${location},`;
        return (section)=>{
            return `${name}${location} Please visit ${section} section`
        }
    }
}

formWelcomNote('Yester')('VK Just Code Articles')('JS Articles');
> 'Hello Yester, Welcome to VK Just Code Articles, Please visit JS Articles section'
```
**Example 3:**

```typescript
function calculation(fn) {
    switch (fn) {
        case 'add': return (a, b) => a + b;
        case 'sub': return (a, b) => a - b;
        case 'mul': return (a, b) => a * b;
        case 'div': return (a, b) => a / b;
    }
}
console.log(calculation('mul')(4, 2));
```

**8. Partial Application:**

You fix a certain number of arguments for a function and generate a new function with fewer parameters. This new function can be called with the remaining arguments at a later time. Partial application helps in creating more specialized and reusable functions.

**Example:**

```typescript
function add(a, b) {
  return a + b;
}

// Partially apply the first argument
const add2 = multiply.bind(null, 2);

console.log(add2(5));  // Output: 7 (2 + 5)
console.log(add2(8));  // Output: 10 (2 + 8)
```

**9. Referential Transparency:**

An expression in javascript can be replaced by its value is called referential transparency.

```typescript
const add = (x,y)=>x + y;

const multiply = (x)=>x * 4;

// add (3, 4) can be replaced by 7. - Referential Transparency.

multiply(add(3, 4)); 
> 28

multiply(add(3, 4));
> 28
```

```typescript
const arr = [];
const add = (x,y)=>{
    const addition = x + y;
    arr.push(addition);
    return addition;
}

const multiply = (x)=>x * 4;

// Here, we can't replace add(3,4) with 7 as it affects the program
multiply(add(3, 4));
> 28

> multiply(add(3, 4));
28
```

**10. Closure:**

Closure gives you access to an outer functions scope from inner function.

```typescript
function outer() {
    const name = 'test';
    function inner() {
     // 'name' from outer function is accessible inside inner function
     console.log(name);
    }
    inner();
}
outer();

> test
```
```typescript
function outerAdd(x) {
    return function(y) {
        return x + y;
    };
}

const outer12 = outerAdd(12); // x as 12.
const outer14 = outerAdd(14); // x as 14.

const outer12Result = outer12(12); // y as 12.
console.log(outer12Result);
> 24

const outer14Result = outer14(14); // y as 14.
console.log(outer14Result);
> 28
```
Alternatively, you can use arrow functions as well like below.

```typescript
outerAdd = x => y => x + y;

const outer12 = outerAdd(12);
const outer14 = outerAdd(14);

const outer12Result = outer12(12);
console.log(outer12Result);
> 24

const outer14Result = outer14(14);
console.log(outer14Result);
> 28
```
Counter example using closure:

```typescript
function outer() {
    let counter = 0;
    return function inner() {
        counter += 1;
        return counter;
    }
}
const out = outer();

console.log(out());
console.log(out());
console.log(out());

> 1
> 2
> 3
```

**11. Recursion:**

Recursion is a programming technique in which a function calls itself to solve a problem.

**Example:**

```typescript
function factorial(n) {
  if (n === 0 || n === 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}

console.log(factorial(5));  // Output: 120 (5 * 4 * 3 * 2 * 1)
console.log(factorial(0));  // Output: 1 (by definition)
```
In this example, the factorial function calculates the factorial of a given number n. It uses the base cases of n === 0 and n === 1, where the factorial is defined to be 1. For any other value of n, the function recursively calls itself with n - 1 and multiplies the result by n.

When you call factorial(5), the sequence of recursive calls looks like this:

```typescript
factorial(5)
  -> 5 * factorial(4)
       -> 4 * factorial(3)
            -> 3 * factorial(2)
                 -> 2 * factorial(1)
                      -> 1
                 <- 2 * 1 = 2
            <- 3 * 2 = 6
       <- 4 * 6 = 24
  <- 5 * 24 = 120
```
please do comment any examples on any concept.

Thanks.
[urstrulyvishwak](https://twitter.com/urstrulyvishwak)