Functional Programming
in Javascript
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
Why Functional Programming?
- Concise
- Less code for bugs to hide
- Easier to Test
- Easier to Read
- Easier to Reason About
- Easier to Maintain
- Better Reuse
- It is catching up fast [again]
- You'll read a lot FP code at work
- You don't wan to be left behind
FP roots in Javascript
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.
First Class Functions
let print=console.log;
print("How's that?");
let print2=function newPrint(value){
console.log(value);
}
print2("How about now?");
Functions can be assigned to variables
First Class Functions
let timeout=function(fn,waitTime,...args){
waitFor(waitTime);//<- own implementation
fn(...args);
}
timeout(console.log,10000,"abstract");
setTimeout(console.log,10000,"abstract");
Functions can be passed as parameters
First Class Functions
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
Functions can return other functions
Higher Order Functions
-
Take functions as parameters
-
Return new functions
-
Or do both!
Partial Application
const remove = delimiter =>
str => str.replace(delimiter, "");
const deleteDot = remove(".");
deleteDot("Hell.o"); //Hello
A function partially applies arguments and returns another function
let remove = (delimiter,str) => str.replace(delimiter, "");
remove(".","Hell.o"); //Hello
Full Application
Partial Application
State Change
- Explicitly Changing Global Scope
- Local copy of environment & state change
- Implicitly Changing referenced variables (side effects)
Global State Change
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"); //
Local State Change
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"); //
Global Vs. Local
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"); //
Closures
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
Closures
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!
Partial Application
const remove = delimiter =>
str => str.replace(delimiter, "");
const deleteSpace = remove(" ");
deleteSpace("Hell o"); //Hello
const deleteDot = remove(".");
deleteDot("Hell.o"); //Hello
Makes heavy use of closures!
Inner functions returned from 'remove' have closure over 'delimiter'
Implicit State Change
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
Implicit State Change
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
Pure & Impure Functions
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
Avoiding Side Effects
- ImmutableJS
- Object.assign
- Array.slice
Use pure functions as much as possible (80%)
Very limited (20%) and isolated impure functions to manage state, logs and I/O.
Recursion
To understand recursion, you first need to understand recursion. - John D. Cook
No More Iterative Loops
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
Recursion
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.
Recursion
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!
Tail Call Optimization
function loop(limit,n=1) {
if (n >= limit) return;
console.log("loop", n);
loop(limit,n + 1);
}
- The last (tail) line is the recursive call.
- Just one (tail) stack frame
- It is part of ES6.
- But browser support is very minimal.
function loop(limit, n=1) {
if (n >= limit) return;
loop(limit,n+1);
console.log("loop", n);
}
Optimized (Tail Call)
Inefficient (& output is different)
Divide & Conquer
let sort=(arr)=>{
if(arr.length < 2 ) return arr;
let [left,right]=divide(arr);
return conquer(sort(left),sort(right));
}
- Divide the problem until it is small enough to solve
- Solve the problem
- Compose solutions
No More Loops?
Not Really.
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.
Patterns
Map
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]
Array is a Functor
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]
- Container implements a map method
- map returns similar container (context)
- return from map can be chained
- map is agnostic about the value contained
Filter
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'
Reduce
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'
Reduce
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.
Compose
var psd=design();
var repo=build(psd);
deploy(repo);
deploy(build(design()));
Compose
function compose(f1,f2,f3){
f1(f2(f3()));
}
compose(deploy,build,design);
Compose At Scale
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
Compose
const compose=(...fun)=>{
return (v)=>fun
.reduceRight((acc,fn)=>fn(acc),v);
}
const squareLast=compose(square,last);
Pipe
function pipe(f1,f2,f3){
f3(f2(f1()));
}
pipe(design,build,deploy);
Pipe
const pipe=(...funs)=>{
return (v) => funs
.reduce((args,fn)=>fn(args),v);
}
const squareLast=pipe(last,square);
Combinators
- Combinator is a higher-order function
- Only functions as arguments
- Returns another function
- Returned function is usually a combination of functions passed in as arguments
- Compose, Pipe...etc.
Unary
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);
Decorators
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.
Higher Order Functors
let iPromise=new Promise(
(resolve,reject)=>{});
iPromise.then(callback1)
.then(callback2)
.then(callback3)
.catch(errorHandler);
$('#id')
.css(x,y)
.on('click')
.show();
Boxed and Locked
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...
Monads
- Enhanced Functors (A step above Arrays)
- Have FlatMap functionality
- Return Same Type Of Monads
- Hence, Chainable
Just a design pattern that makes it easy to stitch complex functionalities together!
Monads
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
Libraries
- Ramda
- Loadash/fp
- Monet.js
- Underscore
References
- A whole wealth of wisdom in JavaScript Allongé
- FunFunFunction Youtube Channel & Nordic.js talk
- Marvelously Mysterious Monads
Thank You

Functional Programming in Javascript
By Vijayabharathi Balasubramanian
Functional Programming in Javascript
Introduction to functional programming principles and how to apply them on JavaScript programs.
- 1,077