Functional Programming in JAVASCRIPT

What is functional programming?
In simple words, functional programming means which separates data and functions (separates data and behavior).

FP Principles:
  1. No side effects.
  2. For the same input always function should return the same output.
  3. Function should have a single task.
  4. Function should be pure.
  5. No shared state.
  6. Immutable state.
  7. Composability.
  8. Predictable result.
When to use functional programming?
  1. Data processing. 2. Inside Concurrent System. 3.High criticality Systems. 4. Serverless applications.

let's discuss some main terminologies used in functional programming:

1. Pure functions

Pure Function is a function(a block of code) that always returns the same result if the same arguments are passed. It always depends on input argument, for same input always it returns same output.

const multiply = (a, b) => a * b;

2. Idempotent

A function is said to be idempotent if it returns the same output for the same input or does what we expect it to do.
Idempotent is different from pure function as it allows side effects. An example could be calling an API with an input and returning the same output no matter how many times it has been called.

3. Imperative and Declarative

Imperative it will tell what to do and how to do task. Ex:

for(let i = 0; i < 5; i++)  {
   console.log(i);
}

In above for loop we are explicitly telling variable initialization, condition check and incrementing.

Declarative tells whats to do and what should happen.

[1,2,3,4,5].forEach(i=>console.log(i))

Normally in functional programming we are using declarative code.

4. Immutability

Immutable data cannot change its structure or the data in it.
In the FP, we create data or objects by initializing them. When we are using those, we do not change their values or their state. If we need, we'll create a new one, but we do not modify the existing object's state. Ex:

const obj ={name:"Pranav"} 
function clone(obj) { return {...obj} }

Every time creating new object(copy) it's difficult if that object is heavy. For this we can use > structural sharing.

5. Referential Transferency

Referential Transparency is the property that lets you replace an expression with its value, and not change the results of whatever you were doing.

6. Higher Order functions(HOF)

HOF are functions that can take functions as params or return functions.

const logger = function (val) {
  console.log(val);
};

function caller(fn, value) {
  fn(value);// it returns function as return value
}
caller(logger,"hello");// caller taking logger function as param

7. Closure

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state(the lexical environment).
In other words, a closure gives you access to an outer function’s scope from an inner function.
In JavaScript, closures are created every time a function is created, at function creation time.
Features of closures:
1. Memory efficient
2. Encapsulation

// * Memory efficient
function heavyDuty(index) {
  const bigArray = new Array(7000).fill("😃");// 
  console.log("every time new values assigned to bigArray....");
  return bigArray[index];
}
function heavyDutyHelper() {
  const bigArray = new Array(7000).fill("😃");
  console.log("creating  array at once...");
  return function (index) {
    return bigArray[index];
  };
}
console.log(heavyDuty(10));
console.log(heavyDuty(10));
let getHeavyDuty = heavyDutyHelper();
console.log(getHeavyDuty(10));
console.log(getHeavyDuty(10));


// 2. Encapsulation
const makeNuclearButton = () => {
  let timeWithoutDestruction = 0;
  const launch = () => {
    timeWithoutDestruction = -1;
    return "🧨";
  };
  const totalPeaceTime = () => timeWithoutDestruction;
  const passTime = () => {
    timeWithoutDestruction++;
  };
  setInterval(passTime, 1000);

  return {
    totalPeaceTime,
    //launch
  };
};

const ohno = makeNuclearButton()
console.log(ohno.totalPeaceTime());// ohno.launch() this is encapsulated.

8. Currying

Currying is a technique to translate the evaluation of a function that takes multiple arguments into evaluating sequence of function each with single argument.

//Normal Function
 const multiply = (a, b) => {
    return a * b;
 }
//Using curry
const curriedMultiply = (a) => (b) => a * b;

/**Using curriedMultiply function we can create multiple utility functions. For example */

const multiplyByTwo = curriedMultiply(2);
const multiplyByFive = curriedMultiply(5);
console.log(multiplyByTwo(3),multiplyByFive(3))

9. Partial application

Partial application process of producing fun using smaller number of parameters.
The difference between curry and partial application is in curry it expect one argument at a time, But in partial application on second call it expect all arguments.

const multiply=(a,b,c)=>a*b*c;
const partialMultiplyBy5 = multiply.bind(null,5);
console.log(partialMultiplyBy5(2,10))

10. Memoization

Memoization is a specific form of caching that involves caching the return value of a function based on its parameters.
Basic Memoization ex:

function addTo10(n) {
    console.log('long time')
    return n + 10;
}
let cache = {};
function memoizeAddTo10(n) {
    if (n in cache) return cache[n];
    cache[n] = addTo10(n);
    return cache[n];
}
console.log(memoizeAddTo10(5))
console.log(memoizeAddTo10(5))

In the above example cache is global variable we can improve this by using closures:

function memoizeAddTo10() {
    let cache = {};
    return function (n) {
          if (n in cache) return cache[n]
          cache[n] = addTo10(n);
          return cache[n];
    }
}
let memoize = memoizeAddTo10()

console.log(memoize(5))
console.log(memoize(6))
console.log(memoize(5))

11. Composition

Composition is about creating small functions and creating bigger and more complete functions with them.

const user = {
     name: "pranav",
     active: true,
     cart: [],
     purchases: []
}
const compose = (f, g) => (...args) => f(g(...args))
const purchase = purchaseItem(emptyCart, buyItem, applyTaxToItems, addItemToCart)(user, { name: "laptop", price: 200 });


function purchaseItem(...fns) { return fns.reduce(compose) }

function addItemToCart(user, item) {
     const updatedCart = user.cart.concat(item)
     return Object.assign({}, user, { cart: updatedCart })
}

function applyTaxToItems(user) {
     const { cart } = user;
     const taxRate = 1.3;
     const updatedCart = cart.map(({ name, price }) => {
          return { name, price: price * taxRate }
     })
     return Object.assign({}, user, { cart: updatedCart })
}

function buyItem(user) {
     return Object.assign({}, user, { purchases: user.cart })
}

function emptyCart(user) {
     return Object.assign({}, user, { cart: [] })
}

12. Arity

The term arity refers simply to the number of parameters in the definition of a function. This is casually expressed as how many arguments a function takes.

..................................................................
That's it for this post. In my next posts I'll try to explain all the above 12 concepts in depth.

Thank You

21