Samuel Silva
Part 01
In computer science, functional programming is a programming paradigm
that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming approach, which means programming is done with expressions or declarations instead of statements.
Wikipedia
const users = [...];
const candidatesInUS = [];
const allowedWork = [];
const haveJsAndReact = [];
for(user of users) {
if (user.country === 'US') {
candidatesInUS.push(user)
}
}
for(user of candidatesInUS) {
if (user.allowedWork) {
allowedWork.push(user)
}
}
for(user of allowedWork) {
let hasJs = false;
let hasReact = false;
for(skill of user.skills) {
if (skill === 'JavaScript') {
hasJs = true
} else if (skill === 'React') {
hasReact = true
}
}
if (hasJs && hasReact) {
haveJsAndReact.push(user)
}
}
console.log('Candidates in US', candidatesInUS.length);
console.log('Allowed Work', allowedWork.length);
console.log('JS and React Experience,', haveJsAndReact.length);
console.log('Candidates', haveJsAndReact)const filterInUs = compose(
logger('Candidates In US'),
filter(({country}) => country === 'US')
)
const filterAllowedWork = compose(
logger('Allowed Work'),
filter(({allowedWork}) => allowedWork)
)
const filterJSAndReactSkills = ({skills}) => {
return contains('JavaScript')(skills) && contains('React')(skills)
};
const filterReactAndJS = compose(
logger('JS and React Experience'),
filter(filterJSAndReactSkills)
)
const filterCandidates = compose(
filterReactAndJS,
filterAllowedWork,
filterInUs
);
console.log('Candidates', filterCandidates(users));An expression is referentially transparent if you can replace that expression with its corresponding value without changing the behaviour of the program.
// Without Referential Transparency
function addOne() {
let i = 10;
i++; // evaluates 10.
return i;
}
function addOne() {
let i = 10;
10; // Replacing the value of i with the evaluation value will change the result of this function
return i;
}
// With Referential Transparency
function addOne2() {
let i = 10;
let j = i + 1 // evaluates 11
return i;
}
function addOne2() {
let i = 10;
let j = 11; // Replacing the value of the statement with the evaluation result will not change function's return
return j;
}
To fully understand Referential Transparency we need to learn a few important concepts:
A function is considered "pure" if it satisfies two conditions:
- it always returns same result given same arguments.
- it doesn't produce any side effects (which include things like input/output or mutating data).
const sum = (a, b) => a + b;
const subtract = (a, b) => a - b;
const average = (a, b) => (a + b) / 2;
const currySum = a => b => a + b
const currySubtract = a => b => a - b;
sum(1, 2);
average(3, 4);
subtract(2, 1);
currySum(1)(2);
currySubract(2)(1);const value = 0;
const sum = (a) => value + b;
const showValue = () => {
console.log(value);
};
sum(1);
showValue()An immutable object is an object whose state cannot be modified after it is created. All operations will in fact create new objects rather than change it.
let user = {
firstName: 'Samuel',
lastName: 'Castro'
}
// Immutability
const changeName = firstName => lastName => {
return {
firstName,
lastName
}
}
// Mutability
const changeName = firstName => lastName => {
user.firstName = firstName;
user.lastName = lastName;
return user;
}
console.log('Immutable', changeNameImmutable('New', 'Name') === user); // ?
console.log('Mutable', changeNameMutable('New', 'Name') === user); // ?const hobbies = [
'programming',
'reading',
'music'
];
const firstTwo = hobbies.splice(0, 2);
console.log(firstTwo); // ['programming', 'reading']
console.log(hobbies); // ['music'] // original array was mutatle :(const hobbies = Object.freeze([
'programming',
'reading',
'music'
]);
const firstTwo = hobbies.splice(0, 2);
console.log(firstTwo); // ['programming', 'reading'] // TypeErrorImmutable.js provides many Persistent Immutable data structures including: List, Stack, Map, OrderedMap, Set, OrderedSet and Record.
These data structures are highly efficient on modern JavaScript VMs by using structural sharing via hash maps tries* and vector tries* as popularized by Clojure and Scala, minimizing the need to copy or cache data.
* at high level, hash maps and vector tries are implemented algorithms to map over collections/arrays using economic memory usage and performance improvements.
const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = Map({ a: 1, b: 2, c: 3 })
map1.equals(map2) // true
map1 === map2 // falseOn functional programming all operations should be immutable, so does not make sense to use loop statements like for/while, therefore we need to use recursion.
Factorial
4! = 4 x 3 x 2 x 1 = 24
// Using loops operations
const factorial = (n) => {
let result = 1;
while(n > 1) { // :(
result *= n
n--
}
return result
}
console.log(factorial(4)); // 24Factorial with Recursion
const factorial = (n) => {
if (n < 2) return 1
return n * factorial(n - 1)
}
factorial(4); // 24However there is a problem!
It's pure, we're not mutating data and It's declarative.
What happen if I try call factorial(20000)
const factorial = (n) => {
if (n < 2) return 1
return n * factorial(n - 1)
}
factorial(20000); // Uncaught RangeError: Maximum call stack size exceeded4 * factorial(3)
4 * 3 * factorial(2)
4 * 3 * 2 * factorial(1)
4 * 3 * 2 * 1
4 * 3 * 2
4 * 6
24
This algorithm runs in the order of O(n) meaning that it's not scalable at all
Tail-call optimization is where you are able to avoid allocating a new stack frame for a function because the calling function will simply return the value that it gets from the called function
const factorial = (n, acc = 1) => {
if (n < 2) return acc
return factorial(n - 1, n * acc)
}
console.clear();
console.log(factorial(20000)); // Infinityfactorial(4)
factorial(3, 4)
factorial(2, 12)
factorial(1, 24)
24
ECMAScript 6 offers tail call optimization, where you can make some function calls without growing the call stack
Higher Order Functions.
Partial Application and Curry functions.
Point Free programming.
Function Composition
Working with Ramda.js
In mathematics and computer science, a higher-order function is a function that does at least one of the following:
- takes one or more functions as arguments (i.e. procedural parameters),
- returns a function as its result.