Functional Programming (FP) is a paradigm used for build computer applications applying and composing pure functions without mutating the state and data. It is also a declarative type of programming that implements the logic of process without explaining its control flow. It makes use of expressions instead of statements.
There's some concepts that are needed to be understood if FP will be applied:
- Pure functions
- Immutable data
- Referencial transparency
- First class functions
- High order functions
- Recursion over loops
Pure functions
A pure function has two principal characteristics:
1. A function that given the same inputs, return the same output. For Example:
const double = x => x * 2
console.log(double(4)) // 8
console.log(double(4)) // 8
console.log(double(4)) // 8
As you can see, when the function double
is called with the same parameter (in this case 4), it always return the same result.
Not all functions fulfill this specification. This are some examples of functions that are not pure:
Math.random() // 0.9475128240189292
Math.random() // 0.1712299774003645
Math.random() // 0.16032971104683935
(new Date()).getTime() // 1620616533928
(new Date()).getTime() // 1620616539849
(new Date()).getTime() // 1620616541638
In this case, the functions random
and getTime
aren't pure, because given the same parameters are not returning the same value.
2. It has no side-effects. This means that functions are not going to change any arguments, global variables or do some kind of I/O.
const animals = ["cat", "dog", "cow"]
const addSheepAnimal = (animalsArray) => {
animalsArray.push('sheep')
return animalsArray
}
const newAnimals = addSheepAnimal(animals)
console.log(newAnimals) // [ 'cat', 'dog', 'cow', 'sheep' ]
console.log(animals) // [ 'cat', 'dog', 'cow', 'sheep' ]
In the example above, the function addSheepAnimal
has side effect over the animalsArray
parameter, because is changing the array that is pass when the function is called. In order to fix this, the array should be clone inside the function so it has no side effects.
const animals = ["cat", "dog", "cow"]
const addSheepAnimal = (animalsArray) => {
return [...animalsArray, 'sheep']
}
const newAnimals = addSheepAnimal(animals)
console.log(newAnimals) // [ 'cat', 'dog', 'cow', 'sheep' ]
console.log(animals) // [ 'cat', 'dog', 'cow' ]
Immutable data
Data is immutable when values of javascript structures cannot be changed once are assigned. This means when a variable is already assigned, the value can't be change in coming parts of the flow.
It's a good practice always use const
declaration instead of var
and let
so mutable data can be avoid. But we must have caution with arrays and objects in order to not change the pointer reference, is recommended to clone the object/array once is passed as parameter in the functions if a change needs to be made on them, and then return the new object.
Referencial transparency
The sum of Pure Functions
plus Immutable Data
returns Referencial Transparency
. The function are not going to modify any global variables, and only will work with their scope variables. Once the function returns a value, it will be assigned to a new variable. Example:
const number = 3;
const square = x => x ** 2;
const result = square(number)
console.log(result) // 9
First-class functions
First-class functions
are functions that are treated as variables and can be passed to another functions as arguments.
const numbers = [2, 5, 3, 8, 10];
const double = (x) => x * 2;
const doubleNumbers = numbers.map(double);
console.log(doubleNumbers) // [ 4, 10, 6, 16, 20 ]
In the example above, the double
function is considered as first-class function
because is treated as variable, and also is being passed in the map
function.
High order functions
High-order functions
are functions that accept another functions as arguments or returns a function as an output.
Array procedures like map
, reduce
or filter
are considered as high-order functions
. Also, methods can be created that match this definition.
Recursion instead of loops
In functional programming is needed to avoid using loops, because they are required to change the state outside their scope for the purpose to finish the cycles.
// Loop
let loopSum = 0
const loopLimit = 15
for (let i = 0; i <= loopLimit; i++) {
loopSum += i;
}
console.log(loopSum) // 120
// Recursion
const recLimit = 15
const sumNum = (val, lim) => {
if(val <= lim) {
return val + sumNum(val + 1, lim)
}
return 0
}
console.log(sumNum(0, recLimit)) // 120
In this previous script, we did both loop and recursion example. We can notice how loop change the global variable loopSum
in every cycle. Instead the recursive way not changed any state that didn't belong to its scope.
Wrapping up
I hope you enjoyed this information about functional programming in JavaScript with me! For some it may be a new programming paradigm, but I hope you will try to use it. I think your applications will be easier to read and debug. If you have any suggestions, comments or questions, let me know in the comments. Thanks!
Top comments (0)