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")); //Hello
const remove = delimiter =>
str => str.replace(delimiter, "");
const deleteDot = remove(".");
deleteDot("Hell.o"); //Hello
let remove = (delimiter,str) => str.replace(delimiter, "");
remove(".","Hell.o"); //Hello
Full 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"); //Hello
Inner 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 exceeded
There 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 copy
Reduce 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]); //64
const 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