JavaScript Functional Programming

Introduction

Functional Programming with JavaScript

Contents

  1. Functional Programming
  2. Pure Functions
  3. Idempotence
  4. Imperative vs Declarative
  5. Immutability
  6. Currying in JavaScript
  7. Partial Application
  8. Memoization with cache and closure
  9. Arity in JavaScript
  10. Compose and pipe in JavaScript

1. Functional Programming

What is Functional Programming?

In computer sciencefunctional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that map values to other values, rather than a sequence of imperative statements which update the running state of the program.

Learn more: https://en.wikipedia.org/wiki/Functional_programming

composing pure functions, avoiding shared state, mutable data, and side-effects.

  1. Easier to determine inputs.
  2. Easier to determine outputs.
  3. Easier to demonstrate and prove you have a correct program.
  4. Easier to test programs too difficult to prove.

Why is functional programming so great?

If you try to perform with memory efficiency and logic at the same time, the problem is that you may create hidden side effects which cause a lot of bugs. Thus, the idea in functional programming of keeping functions small, pure, and composable doing one thing at a time and doing it well this idea of immutability the idea that functions that take inputs and output. So the function can be used with other functions. It allows us to have a predictable program where it minimizes bugs.

2. Pure Functions

In computer programming, a pure function is a function that has the following properties:[1][2]

  1. the function return values are identical for identical arguments (no variation with local static variablesnon-local variables, mutable reference arguments or input streams), and
  2. the function application has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or input/output streams).

No side Effects

Learn more: https://en.wikipedia.org/wiki/Pure_function

Side effects

const array = [1,2,3];

function mutateArray(arr){
    arr.push(5);
}

mutateArray(array);
console.log(array);
[ 1, 2, 3, 5 ]

The function has what we call side effects and the side effects are does the function modify anything outside itself.

const array = [1,2,3];

function mutateArray(arr){
    arr.forEach(i => {
        arr.push(i);
    });
}

mutateArray(array);
console.log(array);
[ 1, 2, 3, 1, 2, 3 ]

This is confusing because we should figure out how the function is modifying the array data.

This is one the problems with having side effects is that reusing shared state something like a global variable that can interact with anything and the order of function calls matter and that can cause a lot of bugs.

No side effects

const array = [1,2,3];

function addItem(arr){
    const newArray = [].concat(arr);
    newArray.push("***");
    return newArray;
}

console.log(addItem(array));
console.log(array);
[ 1, 2, 3, '***' ]
[ 1, 2, 3 ]

Although we created a new state or new data inside of the function it is local variable and the function is not modifying anything outside of our scope world.

Also, there is we call referential transparency and referential transparency simply means the same input and always the same output.

The rule no matter what the input if they are the same it is always going to give me the same output and as a matter of fact the functions also have no side effects.

function plus(n1, n2) {
    return n1 + n2;
}
function muliply(n) {
    return n * n;
}
console.log(muliply(plus(2, 6)));
64
  1. Easy to predict.
  2. Easy to test.
  3. Avoid a lot of bugs.

Can everything be pure?

  • console.log() which is a side effect.
  • As a matter of fact, input and output are a side effect that is communicating with the outside world.

Philosophically, without side effects, it does not do anything because a program can not exist without side effects. You can not run a piece of code without having a side effect of interacting with the browser. You can not have a Web site with just pure functions. So the goal of functional programming is not to make everything pure functions. The goal is to minimize side effects. The idea is to organize your code with there is a specific part that has side effects but when you have a bug you know right away to go to that spot because that is where the side effects are happening.

To build programs that are built with a bunch of very small reusable predictable pure functions.

  1. Pure
  2. No shared state
  3. Immutable state (Never modify global state)
  4. Composable
  5. Predictable
  6. Return statement

3. Idempotence

What is Idempotence? Given the same inputs with the same output. It is similar to pure function but it is a little bit different.

Not Idempotence function

function notIdempotence(n){
    return Math.random(n);
}

console.log(notIdempotence(1));
the result is continually changed.
  • This is not idempotence.

Idempotence function but not pure

function idempotence_but_not_pure(n){
    return console.log(n);
}
idempotence_but_not_pure(7);
7

This function is idempotence. However, this function is not pure function because console.log().

# Another thing that can be idempotent for example is deleting a user from a database. Idempotence, you see a lot in APIs, like HTTP get requests.

4. Imperative vs Declarative

Imperative code: Tell the machine what to do and how to do it.

Declarative code: Tell the machine what to do what should happen. But It does not tell the computer how to do things.

Computers are better at being imperative that is it needs to know how to do things. Humans are more declarative.

If you order pizza, you do not have to tell how to make pizza.

Machine code is very imperative. more higher level languages are more declarative. You do not have to say this is where you should store the memory.

Imperative code

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

This is pretty imperative: we say declare variable zero and then loop for a ten times and then also increment by 1. Also, console.log(i) value. This is a lot of instruction.

More Declarative code

[1,2,3,4,5,6,7,8,9,10].forEach(i => console.log(i));

This is more declarative. I order console.log(i) a array value with array values times.

Jquery is a lot of more imperative than React, Angular, and Vue.

Jquery we told our website more how to do.

React is fairly declarative. You do not tell how to do display.

Functional programming helps us be more declarative with composing function. Declarative code is always going to end up either compiling down or being processed by machine code. So, Eventually have to compile and do imperative things.

The idea of declarative code

  1. Easier to read and
  2. More productive

5. Immutability

Immutability means not changing the state (the value of its properties). Also, not adding new properties to the state.

By copy the object, The state is not changing.

const object = { name: "Tim" };

function copyObject(a) {
  return { ...a }
}

console.log(copyObject(object));
console.log(object);
{ name: 'Tim' }
{ name: 'Tim' }

The idea of immutability that is not changing the state but instead making copies of the state and returning a new state every time.

Update Copied Object

const object = { name: "Tim" };

function copyObject(a) {
  return { ...a }
}

function updateObject(a){
  const newObject = copyObject(a);
  newObject.name = "Mike";
  return newObject;
}

console.log(updateObject(object));
console.log(object);
{ name: 'Mike' }
{ name: 'Tim' }

The original data is never changed.

6. Currying in JavaScript

Currying is a technique of translating the evaluating of a function that takes multiple arguments into evaluating a sequence of functions each with a single argument.

const sum = (a, b) => a + b;

// with Currying in JavaScript
const curriedSum = (a) => (b) => a + b;
console.log(curriedSum(3)(2));
5

The function from taking multiple parameters to taking a parameter at a time.

You can create multiple utility functions out of this

const curriedSum = (a) => (b) => a + b;
const sumBy5 = curriedSum(5)
console.log(sumBy5(2));
7

7. Partial Application

Partial application is a function that takes a function with multiple parameters and returns a function with a smaller number of parameters. 

It is a process of producing a function with a smaller number of parameters. It means taking a function applying some of its arguments into the function so it remembers those parameters and then it uses closures to later on be called with all the rest of the arguments.

const multiply = (a,b,c) => a * b * c;
const partialMuliplyBy5 = multiply.bind(null, 5);
console.log(partialMuliplyBy5(3,4));
60

I have partially appiled a parameter the “a” parameter and then I get to call the rest of the parameters “b” and “c”. That is the main difference between currying and partial application.

  • Partial application is all the arguments on the second call.
  • Currying is only one argument at a time.

8. Memoization with cache and closure

In order to understand how dynamic programming works we need to understand what caching means “caching” is a way to store values so you can use them later on.

function multiplyTwo(n) {
  console.log("take calculation time");
  return n * 2;
}

console.log(multiplyTwo(5));
console.log(multiplyTwo(5));
console.log(multiplyTwo(5));
take calculation time
10
take calculation time
10
take calculation time
10
  • We call the same function 3 times.

Cache or Memoization

let cache = {
  1: 2,
  2: 4,
  3: 6,
};

function cachedMultiplyTwo(n) {
  if (n in cache) {
    return cache[n];
  } else {
    console.log("take calculation time");
    cache[n] = n * 2;
    return cache[n];
  }
}

console.log(cachedMultiplyTwo(5));
console.log(cache);
console.log(cachedMultiplyTwo(5));
console.log(cachedMultiplyTwo(5));
take calculation time
10
{ '1': 2, '2': 4, '3': 6, '5': 10 }
10
10
  • Object (Hash Table) Big O is O(1) which is fast.

What is memorization?

memorization is a specific form of caching that involves caching the return value of a function that is the return value of a function that is the return value of the function based on its parameters, and if the parameter of the function does not change it is memorized it uses the cache because it is calculated the same thing before with the same parameter.

With memorization, the function does not have to calculate it again.

Ideally, you do not want to file the cache in Global Scope. That is to be living outside the function. Ideally, it is good practice to have memory or the cache to live inside of the function. There are many ways to do this based on the language. However, in JavaScript that we can use something called closures.

Closures with Memorization

function closureCaheMultiplyTwo() {
  let cache = {};
  return function (n) {
    if (n in cache) {
      return cache[n];
    } else {
      console.log("take calculation time");
      cache[n] = n * 2;
      return cache[n];
    }
  };
}

const cacheForMultiplyTwo = closureCaheMultiplyTwo();
console.log(cacheForMultiplyTwo(2));
console.log(cacheForMultiplyTwo(2));
console.log(cacheForMultiplyTwo(2));
console.log(cacheForMultiplyTwo(3));
take calculation time
4
4
4
take calculation time
6

9. Arity in JavaScript

Arity is the number of arguments or operands that a function or operation in logic, mathematics, and computer science takes.

It means the number of arguments a function takes.

The compose function has two Arity.

const compose = (a, b) => (data) => a(b(data));

The addFive function has one Arity.

const addFive = (num) => num + 5;

In functional programming, although there are no rules of arity, a fewer number of arguments is better.

  1. it makes functions more flexible
  2. it is easy to make compose functions with fewer arguments.

10. Compose and pipe in JavaScript

Function composition is an operation that takes two functions a and b and produces a function h such that h(x) = a(b(x)).

Compose Function

const compose = (a, b) => (data) => a(b(data));
const multiplyBy5 = (num) => num * 5;
const makePositive = (num) => Math.abs(num);

const multiplyBy5andMakePositive = compose(multiplyBy5, makePositive);
console.log(multiplyBy5andMakePositive(-50));
250

The definition compose ability is a system design principle that deals with the relationship between components.

With compose functions, you can build them together to add extra functionality to create that data flow where we take a piece of data we take it through all these functions and them finally we have some sort of data that gets output because all the functions are pure and all the functions are composed of all.

Pipe Function

const pipe = (a, b) => (data) => b(a(data));
const multiplyBy5 = (num) => num * 5;
const makePositive = (num) => Math.abs(num);

const multiplyBy5andMakePositive = pipe(multiplyBy5, makePositive);
console.log(multiplyBy5andMakePositive(-50));
250

Pipe function order is b(a(data)), but Compose function order is a(b(data)). this is just swap the props.

More functions with compose and pipe

Firstly right function argument is executed: compose

Firstly left function argument is executed: pipe

Leave a Reply

Your email address will not be published.

ANOTE.DEV