Functional Programming

Principles

  • Separate data from behavior

  • Use of declarative functions (as opposed to imperative)

  • Proper use of caching (computing efficiency)

  • Pure functions

  • Composing (chained atomic pure operations)

Pure functions

  • Don't mutate state (global scope)

  • Always have a return statement. This is useful for chaining purposes

  • Idempotence. On the same input, return the same output, regardless of how many times executed

  • Usually have 1 argument

//Side effects:
const array = [1,2,3];
function mutateArray(arr) {
  arr.pop()
}
function mutateArray2(arr) {
  arr.forEach(item => arr.push(1
  ))
}
//The order of the function calls will matter.
mutateArray(array)
mutateArray2(array)
array
// Idempotence:
function notGood() {
  return Math.random()
  // new Date();
}

function good() { //pure function
  return 5
}

Math.abs(Math.abs(10)) //pure function

Cache in memory

//Memoization
//learn to cache
function addTo80(n) {
  return n + 80;
}

addTo80(5)

let cache = {};
function memoizeAddTo80(n) {
  if (n in cache) {
    console.log('quickly rerieved from cache');
    return cache[n];
  } else {
    console.log('expensive operation');
    const answer = n + 80;
    cache[n] = answer;
    return answer;
  }
}

Here is a better implementation of caching, making the above pure.

// let's make that better with no global scope.
function memoizeAddTo80(n) { 
  let cache = {};
  return function(n) { //This is closure in javascript.
    if (n in cache) {
      console.log('quickly rerieved from cache');
      return cache[n];
    } else {
      console.log('expensive operation');
      const answer = n + 80;
      cache[n] = answer;
      return answer;
    }
  }
}

const memoized = memoizeAddTo80();
console.log(1, memoized(6))
// console.log(cache)
// console.log('-----------')
console.log(2, memoized(6))

Currying and Composing

Currying in JavaScript transforms a function that takes multiple arguments into a sequence of functions, each taking a single argument. For example, a function f(a, b) becomes f(a)(b). It allows partial application of functions, where you can fix some arguments and pass others later, enabling more flexible function usage.

// add two numbers
const add = a => b => a + b;

//similar to
const add2 = a => {
    return b => {
        return (a + b)
        }
    }

const result = add(2)(3); // => 5

pipe() and compose()

Composing is the process of chaining functions. In other words, through pipe() and compose() we create a pipeline of functions with the output of one function connected to the input of the next. This is the reason for which these functions need to be pure.

//as seen on freecodecamp.org/news/pipe-and-compose-in-javascript-5b04004ac937/

//Let’s write a function that returns someone’s name.
getName = (person) => person.name;
getName({ name: 'Buckethead' });
// 'Buckethead'

//Let’s write a function that uppercases strings.
uppercase = (string) => string.toUpperCase();
uppercase('Buckethead');
// 'BUCKETHEAD'

//What if we want to add a function that gets the first 6 characters of a string?
get6Characters = (string) => string.substring(0, 6);
get6Characters('Buckethead');
// 'Bucket'

//Let’s get really crazy and add a function to reverse strings.
reverse = (string) =>
  string
    .split('')
    .reverse()
    .join('');

reverse('Buckethead');
// 'daehtekcuB'


//chaining all the operations would result in
reverse(get6Characters(uppercase(getName({ name: 'Buckethead' }))));
// 'TEKCUB

Instead of jamming functions within functions or creating a bunch of intermediate variables, let’s pipe all the things!

// Function composition enabling pipe functionality using reduce()
const pipe = (...functions) => input => functions.reduce(
    (acc, fn) => fn(acc),
    input
)
pipe(
  getName,
  uppercase,
  get6Characters,
  reverse
)({ name: 'Buckethead' });
// 'TEKCUB'

compose() is similar, it just deals with the chain in the opposite direction (from right to left).

const g = n => n + 1;
const f = n => n * 2;

const h = x => f(g(x))
h(30); //=> 62

const compose = (f, g) => x => f(g(x));
compose(f,g)(30); //=> 62

Last updated

Was this helpful?