PURE
IMPURE
CURRYING
COMPOSE
DECORATORS
FUNCTORS
CLOSURES
MAP
FILTER
PARTIAL APPLICATIONS
CHAIN
PIPE
REDUCE
RECURSION
TAIL CALL OPTIMIZATION
FIRST CLASS
CITIZENS
COMBINATORS
ONCE
MAYBE
MONADS
Medium :: @vijayabharathib :: Twitter
Blog: pineboat.in
Brenden Eich was supposed to bring in "Scheme", a functional programming language, before Java joined the party.
The result, we have best of both (or many) worlds.
let print=console.log;
print("How's that?");
let print2=function newPrint(value){
console.log(value);
}
print2("How about now?");let timeout=function(fn,waitTime,...args){
waitFor(waitTime);//<- own implementation
fn(...args);
}
timeout(console.log,10000,"abstract");
setTimeout(console.log,10000,"abstract"); const remove = delimiter =>
str => str.replace(delimiter, "");
/*const remove = function(delimiter) {
function substitute(string) {
return string.replace(delimiter, "");
}
return substitute;
};*/
const deleteSpace = remove(" ");
console.log(deleteSpace("Hell o")); //Hello
const deleteDot = remove(".");
console.log(deleteDot("Hell.o")); //Helloconst remove = delimiter =>
str => str.replace(delimiter, "");
const deleteDot = remove(".");
deleteDot("Hell.o"); //Hellolet remove = (delimiter,str) => str.replace(delimiter, "");
remove(".","Hell.o"); //HelloFull Application
Partial Application
let go=true;
function globalOnce(fn){
return v => go ? (go=false, fn(v)) : void 0;
}
let y=globalOnce(console.log);
y("first"); //first
y("second"); //
let z=globalOnce(console.log);
z("third"); //
z("fourth"); //const localOnce = (fn) => {
let go=true;
return v => go ? (go=false, fn(v)) : void 0;
}
let y=localOnce(console.log);
y("first"); //first
y("second"); //
let z=localOnce(console.log);
z("third"); //third
z("fourth"); //
let y=localOnce(console.log);
y("first"); //first
y("second"); //
let z=localOnce(console.log);
z("third"); //third
z("fourth"); //
let y=globalOnce(console.log);
y("first"); //first
y("second"); //
let z=globalOnce(console.log);
z("third"); //
z("fourth"); //
const closureOnce = (fn) => {
let go=true;
return (...v) => go ? (go=false, fn(...v)) : undefined;
}
const launchRocket=closureOnce(setTimeout);
const ignite=()=>console.log("launched");
launchRocket(ignite,60000); //LAUNCHED
launchRocket(ignite,60000); //
launchRocket(ignite,60000); //When you return a function from another, a copy of the parent function environment is created (an enclosure)
This is accessible by the returned function
const construct = (fn) => {
let go=true;
let playArea=true;
let trees=true;
let cave=true;
const animalEnclosure = (animal) => {
if(go){
go=false;
return fn(animal,trees,cave);
}else{
return undefined;
}
}
return animalEnclosure;
}
Exclusive enCLOSURE!
const remove = delimiter =>
str => str.replace(delimiter, "");
const deleteSpace = remove(" ");
deleteSpace("Hell o"); //Hello
const deleteDot = remove(".");
deleteDot("Hell.o"); //HelloInner functions returned from 'remove' have closure over 'delimiter'
const lastOf = (array) => {
array.reverse();
return array[0];
}
let arr=[1, 2, 3];
console.log(arr); // [1, 2, 3]
console.log(lastOf(arr)); // 3
console.log(arr); // [3, 2, 1]A function "accidentally" changes an object passed to it! An impure one!
const square = (n) => {
n=n*n;
return n;
}
let x=10;
console.log(x); // 10
console.log(square(x)); // 100
console.log(x); // 10
let arr=[1, 2, 3];
Passing literals is 'By Value'
Passing objects is 'By Ref' and prone to side-effects
let x=10;
X
10
0xF9E31
[1, 2, 3]
arr
let arr=[1,2,3];
arr.slice(0,1);
console.log(arr); // [1,2,3]let arr=[1,2,3];
arr.splice(0,1);
console.log(arr); // [2, 3]Pure - Array.slice - returns a new array
Impure - Array.splice - mutates the source
Use pure functions as much as possible (80%)
Very limited (20%) and isolated impure functions to manage state, logs and I/O.
for(let i=1; i<=10; i=i+1){
console.log("for",i);
}function loop(n, limit) {
if (n >= limit) return;
console.log("loop", n);
loop(n + 1, limit);
}Statements
Expressions
function loop(n, limit) {
if (n >= limit) return;
console.log("loop", n);
loop(n + 1, limit);
}When a function calls itself, until baseline condition is met.
function loop(n, limit) {
if (n >= limit) return;
console.log("loop", n);
loop(n + 1, limit);
}
loop(0,1000000);
//console.js:116
// throw e;
// ^
///RangeError: Maximum call stack size exceededThere is a limit!
function loop(limit,n=1) {
if (n >= limit) return;
console.log("loop", n);
loop(limit,n + 1);
}function loop(limit, n=1) {
if (n >= limit) return;
loop(limit,n+1);
console.log("loop", n);
}Optimized (Tail Call)
Inefficient (& output is different)
let sort=(arr)=>{
if(arr.length < 2 ) return arr;
let [left,right]=divide(arr);
return conquer(sort(left),sort(right));
}
Use recursions when the data structure demands it, that too when the data set is small.
Such as, recursively reading through the tree structure of a UI menu (Hopefully, it's just about a screen wide).
Use iterative loops elsewhere, at least until better browser support for TCO.
let arr=[1,2,3,4];
let bit_size=arr
.map(n => n.toString(2)) //['1','10','11','100']
.map(s => s.length); // [1,2,2,3]const toBinary = n => n.toString(2);
const charLength = s => s.length;
let arr=[1,2,3,4];
let bit_size=arr
.map(toBinary) //['1','10','11','100']
.map(charLength); // [1,2,2,3]
let vals=[true,0,1,undefined,NaN,Infinity,false,null];
vals.filter(e=>!!e) //[true,1,Infinity]let searchResults=result.filter(product => {
return product.toLowerCase().includes(searchTerm);
});Takes a function that returns 'boolean'.
Retains elements if the function returns 'true'
let sales=[200,300,400,600];
sales.reduce((total,n) => total+n ,0);
//1500
//Syntax
arr.reduce(callback[, initialValue])
callback(accumulator,element,index,array)* Callback should always return a value for 'accumulator'
let sales=[200,300,400,600];
sales.reduce((arr,n) => arr.concat(n) ,[]);
//[200,300,400,600] <- your own shallow copyReduce is the most humble, and quite powerful function that can play the role of both map and filter.
It returns just one value, which could be a literal, an array or an object...or a function.
var psd=design();
var repo=build(psd);
deploy(repo);deploy(build(design()));function compose(f1,f2,f3){
f1(f2(f3()));
}
compose(deploy,build,design);const compose=(...fun)=>{
return (v)=>fun
.reverse()
.reduce((acc,fn)=>fn(acc),v);
}const last=(arr)=> arr.reverse()[0]; //impure
const square= n => n*n;
const cube = n => n * square(n);
const ternary = compose(cube, square, last);
ternary([1, 2]); //64const compose=(...fun)=>{
return (v)=>fun
.reduceRight((acc,fn)=>fn(acc),v);
}
const squareLast=compose(square,last);function pipe(f1,f2,f3){
f3(f2(f1()));
}
pipe(design,build,deploy);const pipe=(...funs)=>{
return (v) => funs
.reduce((args,fn)=>fn(args),v);
}
const squareLast=pipe(last,square);let unary = fn => v => fn(v);
let unaryInt = unary(parseInt);
["1", "2"].map(parseInt); //[1, NaN]
["1", "2"].map((s)=>parseInt(s)); //[1,2]
["1", "2"].map(unaryInt); //[1, 2]//Syntax
parseInt(string, radix);
map(callback(currentValue[, index[, array]]) {},this);let unary = fn => v => fn(v);
let unaryInt = unary(parseInt);
["1", "2"].map(parseInt); //[1, NaN]
["1", "2"].map(unaryInt); //[1, 2]A function that takes another function, and extends it, is a decorator.
Unary takes a function and reduces it to a function that takes only one argument.
let iPromise=new Promise(
(resolve,reject)=>{});
iPromise.then(callback1)
.then(callback2)
.then(callback3)
.catch(errorHandler);$('#id')
.css(x,y)
.on('click')
.show();function Box(x) {
let value = x;
this.unbox = fn =>
new Box(fn(value));
}
Box.of = (v) => new Box(v);
let square=(n)=>n*n;
let v = new Box(2);
let v1 = v
.unbox(square) //4
.unbox(Math.sqrt); //2
v1.unbox(console.log); //2
const binary = n => n.toString(2);
const bits = s => s.split('');
const bitSize = s => s.length;
let b = Box.of(20);
let b2 = b
.unbox(binary) //10100
.unbox(bits) // ['1','0','1','0','0']
.unbox(bitSize); //5
let b3=b2.unbox(console.log); //5
console.log(b3); Box { unbox: [Function] }
b3.unbox(console.log); //undefined
There is no way out of the box...
Just a design pattern that makes it easy to stitch complex functionalities together!
function Box(x) {
let value = x;
this.unbox = fn =>
new Box(fn(value));
this.extract= fn => fn(value);
}
Box.of = (v) => new Box(v);
let square=(n)=>n*n;
let v = new Box(2);
let v1 = v
.unbox(square) //4
.unbox(Math.sqrt); //2
console.log(v1.unbox(console.log));
const binary = n => n.toString(2);
const bits = s => s.split('');
const bitSize = s => s.length;
let b = Box.of(20);
let b2 = b
.unbox(binary) //10100
.unbox(bits) // ['1','0','1','0','0']
.unbox(bitSize); //5
let b3=b2.unbox(console.log); //5
console.log(b3); Box { unbox: [Function] }
b3.unbox(console.log); //undefined
Of is unit & unbox is flatMap