My aim is that after you read this article, you understand what is a functional programming. There is lot of articles for OOP, myself have return an article on OOP, but when i started learning functional programming i started loving it. Its not about OOP vs FP(functional programing) but how to take advantage of both.
In this article, I want to explain about functional programming and how good javascript support functional programming.
What is Functional Programming?
In any program there is two core thing data and behavior. Data could be array, object,hashmap etc. Data can be in any form. Behavior is function that perform operation on data. Functional programming says that data and behavior(function) are two different thing. they should be kept separate. It simply says that you pass data to function it will process it and return new object.
There are many new terms in functional programming learning for first time will be exhaustive but my personal suggestion is that you should give this a try.
Before understanding how Function programming works in javascript and its terms, It is necessary to understand that function are first class citizen in js, and also closure and High order function. If you are unclear about them you can checkout on my blog on this, https://rebrand.ly/javascript-closures-9d89f
Functional programming is all about separation of concerns.It's all about packaging our code into separate chunks so that everything's is well organized in each part of our code.Functional programming says that data and behavior(function) are two different thing. they should be kept separate. The core pillar of functional programming is pure function.
What are pure function?
A function that follow below point are pure function:
- Given the same input it will provide the same output no matters how many times we call it
- It does not modify its outer world that is, it has no side effect. Let's understand with example
function addTwoNumber(num1,num2) { | |
return num1 + num2; | |
} | |
addTwoNmber(3,4); // will return 7 no matter how many times is called | |
function multiplyWithCurrentTime(num) { | |
return num * new Date().getTime(); | |
} | |
multiplyWithCurrentTime(3);// will provide new output each time we call the funciton |
In above I have created two function removeLastItem and immutablyRemoveLastItem.
The removeLastItem has side effect as it modify outer world variable arr
while immutablyRemoveLastItem function has no side effect because it first copy the external variable using concat method and then alter the new array(which it has ownership of) and return it.
// In this example i want to clear out the difference between function with side effect and a function without side effect | |
let arr = [1,2,3]; | |
function removeLastItem(input) { | |
input.pop(); | |
} | |
removeLastItem(arr); | |
console.log(arr); // output [1,2] (this function changes the orignal variable from [1,2,3] -> [1,2]) | |
// above execution has side effect as it mutate arr which belong to the outside world. | |
function immutablyRemoveLastItem(input) { | |
const newArray = [].concat(input); // concat method copies the value to new variable. | |
return newArray.pop(); | |
} | |
let newArr = [1,2,3]; | |
console.log(immutablyRemoveLastItem(newArr)); // output [1,2] | |
console.log(newArr); // output [1,2,3] -> the above function does not have side effect as it not modify the orignal input instead it copies | |
// and alter the copied array. | |
- Let understand with example the concept of same input then same out no matter how many time the function is called
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
function addTwoNumber(num1,num2) { return num1 + num2; } addTwoNmber(3,4); // will return 7 no matter how many times is called function multiplyWithCurrentTime(num) { return num * new Date().getTime(); } multiplyWithCurrentTime(3);// will provide new output each time we call the funciton
There are some terms in functional programming let's define them
Referential Transparency
In functional programming, referential transparency is generally defined as the fact that an expression, in a program, may be replaced by its value (or anything having the same value) without changing the result of the program. This implies that methods should always return the same value for a given argument, without having any other effect.
Let's understand it with example
let a = (num1,num2) => { | |
return num1+num2; | |
} | |
let b = (num) => { | |
return num*2; | |
} | |
console.log(b(a(3,4))) //output will be 14 | |
// here i can replace a(3,4) expression with value 7 value and this will not effect to the result of the program because its return | |
// value is 7 | |
// so i can replace console.log(b(a(3,4))) to console.log(b(7)) as a function is refrencially transparent | |
let c = (num1,num2) => { | |
console.log(`Value of num1:${num1} and value of num2:${num2}`); | |
return num1+num2; | |
} | |
console.log(b(c(3,4))) //output will be 14 | |
// here i cannot replace expression c(3,4) with value 7 as it effect the result of the program | |
// function c has console.log() which is one type of side effect so it is not referentially transparent |
In the above example function a
Is referential transparent as it can be replaced by its value without effecting the result of the program while function c
is not referential transparent because here replacing with the value will effect the result of the program as function c has console.log which is one type of side effect.
Idempotence
A function is idempotence if for same input it provides same output or does what we expect, Idempotence is different from pure function as it allow side effect. A n example could be get api that with same input provide same output no matter how many times it is called.Another Feature of Idempotence is the idea of calling itself again and again and still the output is the same.Let see other example also:
//here i will try to explain a key concept idempotence with example | |
function notIdempotenceFn(num) { | |
return Math.random(num); | |
} | |
notIdempotenceFn(5); | |
notIdempotenceFn(5); | |
// the output of above two function will be differeht however passing the same input so the function is not idempotent | |
function idempotentFn(num) { | |
console.log(num); | |
} | |
// The above function is idempotent because every time we call it, it does the same thing for same input so it is idempotent | |
// it is not pure because it is alter its outside world as console.log will log in window | |
//Another feature of idempotence is the ability to call itself again and again and still the output is the same let see this with | |
// an example | |
function getAbsolute(x) { | |
return Math.abs(x) | |
} | |
getAbsolute(getAbsolute(getAbsolute(-50))) //50 | |
// I am calling getAbsolute function again and again but it output will be the same. |
In the above example there are three function notIdempotenceFn,idempotentFn and getAbsolute. In notIdempotenceFn function will result different output in each call so it is not idempotent while the function idempotentFn is idempotent as for same input it
will have same output that is console.log which will print the output to the console. One note idempotentFn function is not pure as it print in console that is altering the outside world. getAbsolute function is an idempotent function as it provide the same result no matter how many times i call it.
Imperative vs Declarative
Imperative code means what to do and how to do while declarative code means what to do and what need to be done it will not tell how to do it. Let's understand with an example
//Imperative: what to do and how to do | |
//Declarative: what to do and what should be done | |
// Task i want to loop through 1 to 5 and print in the console let see how we can do the code impertively as well as declaratively | |
//Imerative | |
for(i=1;i<=5;i++) { | |
console.log(i); | |
} | |
/* | |
In the above example i tell what to do console.log and how to do it like first declare i = 1, i should be less then 5, increment one by one | |
this is Imerative we have to specify each detail | |
*/ | |
//Declarative | |
[1,2,3,4,5].forEach(item=>console.log(item)); | |
/* | |
In the above example i am telling what to do console.log and here i am not telling how to do it , it is take care by forEach , that's | |
above code is declarative | |
*/ |
In the above example we have one task to console.log
1 to 5 and how this task can be done imperatively and declaratively. The for loop is imperative because here we define what to do that is console.log
and also how to do by defining variable let i=1
, its condition i<=5
and increment by 1 i++
. The other example is forEach
loop which is declarative because here we specify what to do that is console.log
and not how to do which is manage by forEach function.
Why i am teaching you about Imperative vs Declarative because functional programming help us to be more declarative by using compose which we will learn later. compose tell our programs what to do instead of how to do it.
Immutability
Immutability means not to modify original state by copying it and then applying required changes to the new state and returning the new state. Let's see an example
let mutateObj = { | |
first_name:'lorem', | |
last_name:'ipsum' | |
}; | |
let immutateObj = { | |
first_name:'irum', | |
last_name:'egestas' | |
}; | |
const mutatingState = (obj) => { | |
obj.first_name = 'ullamcorper'; | |
return obj; | |
} | |
const immutatingState = (obj) => { | |
const copiedObj = Object.assign({},obj); | |
copiedObj.first_name = 'facilisis'; | |
return copiedObj; | |
} | |
mutatingState(mutateObj); | |
console.log(mutateObj); | |
/* | |
above output { | |
first_name:'ullamcorper', | |
last_name:'ipsum' | |
}; | |
in the above function we have mutated the orignal state | |
*/ | |
const newObj = immutatingState(immutateObj); | |
console.log(immutateObj); | |
/* | |
newObj will be | |
{ | |
first_name:'facilisis', | |
last_name:'egestas' | |
}; | |
and immutateObj will not be changed and it value is | |
{ | |
first_name:'irum', | |
last_name:'egestas' | |
}; | |
here the orignal state is not change | |
*/ | |
In above example we have two function mutatingState and immutatingState. The function mutatingState changes the orignal state while immutatingState function creates a copy of the orignal state and return new state. Functional programming recommends immutability as immutability provide stability and predictability to our code. We will come to know the importance of immutability when we understand composing.
High Order Function
In javascript function are first class citizen. First class citizen means function can be pass as argument,function can be a return value, function can be assign to variable.
What is High Order Function?
A function that receive function as an argument or a function whose return value is function,such function is a High order Function. let's see with an example
const hocFn = function() { | |
return function() { | |
return 5; | |
} | |
} | |
hocFn() // will return function | |
hocFn()() // will return 5 | |
// hocFn return function so it is HOC | |
const fn = function(x) { | |
return x*2; | |
} | |
const hocFn2 = function(fn) { | |
return fn(5); | |
} | |
hocFn2(fn); // will return 10 | |
// hocFn2 accept function as argument it is an HOC |
In above example we have two function hocFn and hocFn2. hocFn function return function so it is HOC while hocFn2 accept function as argument so it is also HOC.
Closure
Like object,Closures in JavaScript are a mechanism for containing some sort of state and in JavaScript we create a closure whenever a function accesses a variable defined outside of the immediate function scope that is the scope of the parent. And it's fairly easy to create a closure.We simply define a function inside another function and expose the inner function either by returning
it or passing it to another function so that we can use that variable.
I have written separate blog on closure be sure to check on that
https://rebrand.ly/javascript-closures-9d89f
Currying
Currying is a technique of translating a function evaluation that takes multiple parameter into evaluating multiple function that each takes a single parameter.
Let's understand with an example
const multiply = (a,b) => a*b; | |
multiply(4,6) // 24 | |
//above is the function that take multiple argument let convert into curring | |
const curring = (a)=>(b)=>a*b; | |
curring(5)(4); // 20 | |
// we have converted multiply function that take 2 param | |
// into two function that takes one parameter at a time |
In above example i have created two function multiply and currying. The multiple function takes two parameter while the currying function take single parameter at time. In this example i have tried to show how we can convert a function with multiple parameter
multiply(a,b)
into multiple function with single parameter curring
.
Partial Application
Partial Application means we are partially applying a function. Suppose a function has 5 arguments. We want its execution to be partially that is for now i will pass 2 arguments and rest of the 3 arguments i will pass later, this is called partial application and it is possible due to closure because when we apply function partially the argument we passed are remembered and are used when we fully execute the function with remaining number of arguments. Let's understand with example.
const multiply = (a,b,c) =>a*b*c; | |
//Partial Application | |
const partiallyMultiplyBy5 = multiply.bind(null,5); | |
partiallyMultiplyBy5(4,10) //200 | |
// in above example partiallyMultiplyBy5 partially apply multiply function with 5 as first argument. | |
// when executing the partiallyMultiplyBy5 function we just have to pass remaining paramater as the first argument 5 has been remember | |
// due to closure. |
In above example partiallyMultiplyBy5 partially apply multiply function with 5 as first argument. When executing the partiallyMultiplyBy5 function we just have to pass remaining parameter as the first argument 5 has been remember due to closure.
Memoization:
Memoization is a special form of caching. Memoization cache the return value of the function based on its parameter that is if the parameter does not change then the return value is memoized. let's see with an example
const notMemoized = (x) => { | |
return x*2; | |
} | |
notMemoized(2); // 4 | |
notMemoized(2); // 4 | |
// every time we run the function the multiplication code get executed which can be optimized by memoization | |
const memoizedFn = () => { | |
const cache = {}; | |
return (x) => { | |
if( x in cache ) { | |
return cache[x]; | |
} else { | |
return x*2; | |
} | |
} | |
} | |
const multiply = memoizedFn(); | |
multiply(2); // 4 | |
multiply(2); // 4 return from cache as the parameter is same so the return value is memoized |
In above example we have two function notMemoized and memoizedFn. notMemoized function will execute the function logic of multiplication for each execution also if the parameter is same. While for memoizedFn the function logic of multiplication will only be executed if the result is not cached ,for second time with same parameter the value will return from cache.
Compose and Pipe
Composing is an idea that describe that the transformation of the data should be obvious. Let's describe compose in simple terms: if there is a data which is processed by a function and that function return new form of the data, the return data is again processed by another function which return new form of data and this chain continues until we get required output. We can say for compose that it is a design principle which describe the relationship with different components (function), here we arrange components in a assembly line which describe how the data is transformed from one function to another.
Pipe is similar to compose the difference is in execution. compose execute the components from right to left while pipe execute the component from left to write.
let see with an example
// Our task is to multiply a number with 3 and take absolute of that number | |
const multiplyWith3 = (x)=>x*3; | |
const getAbsouleOfNum = (x)=>Math.abs(x) | |
// here we have two component multiplyWith3 and getAbsouleOfNum | |
// if we want to do that without compose we will do like these | |
let x = 15; | |
let xAfterMultiply = multiplyWith3(x); | |
let xAfterAbsoulte = getAbsouleOfNum(xAfterMultiply); | |
//Let's do in composable way | |
const compose = (f,g) => data=>f(g(data)); | |
const multiplyBy3andGetAbsolute = compose(multiplyWith3,getAbsouleOfNum); | |
multiplyBy3andGetAbsolute(-15) //45 | |
// Here we have two components multiplyBy3 and getAbsoulteOfNum, we created a realtion between them that is output of absolute function | |
// will be input for multiply by 3 function using compose. | |
//Let's do in Pipe fashion | |
const pipe = (f,g) => data=>g(f(data)); | |
const multiplyBy3andGetAbsolutePipe = pipe(multiplyWith3,getAbsouleOfNum); | |
multiplyBy3andGetAbsolutePipe(-15) //45 | |
// Pipe is similar to compose but difference is that the flow is from left to right in pipe while in compose it is from right to left | |
In above example i have tried to explain how we can use compose to transform the data, In the example there is requirement to multiple a number with 3 and then get absolute of the number. These are two different operation so i have created two function multiplyWith3,getAbsouleOfNum which are pure function. Now if we do not use compose then first we have to call multiplyWith3 function store it output in the variable , then use that variable to call getAbsouleOfNum function to get the desired result, this is a one way to do it. Let's now do in compose way, here we have two component(function) multiplyWith3,getAbsouleOfNum we can arrange them in a sequence in a manner that output of one program is input of another so i have created multiplyBy3andGetAbsolute function which will first execute getAbsouleOfNum and then the output of the getAbsouleOfNum function will be provided to multiplyWith3. We can also do this in the pipe way for that i have created multiplyBy3andGetAbsolutePipe here first muliplyBy3 is executed whose output is passed to getAbsouleOfNum
Arity
Arity mean number of argument the function takes. It is preferred to have less number of argument to a function to make it more usable. My preference for number of argument a function should have is 1 or 2. Let's see with an example
const addNumber = (num1,num2) => num1+num2 | |
const getAbsoulte = (num) => Math.abs(num) | |
// addNumber has arity of two | |
// getAbsoulte has arity of one |
In above example I have created two function addNumber and getAbsoulte. addNumber function has arity of 2 as it has two argument while getAbsoulte has arity of 1 as it has one argument.
Conclusion
Functional programming suggests that the data and function(effect) should be separate. The function should have following properties
- Single task: A Function should be small and should perform single task
- Pure: Function should not have side effect and for same input it should provide same out
- Should have return statement.
- should be compose-able
- Immutable: Function should return a new copy of data and should not change the original state
- Should be predictable
Top comments (4)
Quick correction on idempotence: an idempotent function is not only a function that returns one value for one input. An idempotent function can be applied many times to a value, but all applications beyond the first one yield the same value.
That definition is kinda unclear, so here is an example:
The absolute value function is another good example.
Real world examples can be GET http requests (multiple GET requests can be made that don't effect what they return), or removing elements from a set (first time removing removes it, further attempts to remove it do nothing and the end value of the set is the same).
Thanks for your update
I will edit for the same
Thank you for this useful article, I think that is one of clearest resource about Fp, it's not the best place to start but with some prior experience it's really straight forward, I'll recommend it for sure.
It's greatful that this article is helping you.
And thanks for your positive response. It really motivates