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?