This article will introduce functional programming concepts that every programmer should know. Let's begin by defining what functional programming is (FP from now on). FP is a programming paradigm where software is written by applying and composing functions. A is a "Philosophical or theoretical framework of any kind." In other words, FP is a way for us to think of problems as a matter of interconnecting functions. paradigm Here, I will give a basic understanding of fundamental concepts in FP and some of the problems it helps solve. Note: For practicality, I'll omit specific mathematical properties that define these concepts. This is not necessary for you to use these concepts and apply them in your programs. 1. Immutability A mutation is a modification of the value or structure of an object. Immutability means that something cannot be modified. Consider the following example: cartProducts = [ { : , : , : }, { : , : , : } ] cartProducts.forEach( { currencySign = product.currency === ? : product.price = }) total = cartProducts.forEach( { total += product.price }) .log(total) const "name" "Nintendo Switch" "price" 320.0 "currency" "EUR" "name" "Play station 4" "price" 350.0 "currency" "USD" // Let's format the price field so it includes the currency e.g. 320 € ( ) => product const 'EUR' '€' '$' // Alert! We're mutating the original object ` ` ${product.price} ${currencyName} // Calculate total let 0 ( ) => product // Now let's print the total console // Prints '0320 €350 $' 😟 What happened? Since we're mutating the object, we of . cartProducts lose the original value price . You don't want to call a function in a third party library and not know if it will modify the object you're passing. Mutation can be problematic because it makes tracing the state changes in our application hard or even impossible Let's look at a better option: cartProducts = [...] productsWithCurrencySign = cartProducts.map( { currencyName = product.currency === ? : { ...product, : } }) total = cartProducts.forEach( { total += product.price }) .log(total) const const ( ) => product const 'EUR' 'euros' 'dollars' // Copy the original data and then add priceWithCurrency return priceWithCurrency ` ` ${product.price} ${currencyName} let 0 ( ) => product console // Prints 670 as expected 😎 Now, instead of modifying the original object, we clone the data in the original by using the spread operator cartProducts { ...product, : } return priceWithCurrency ` ` ${product.price} ${currencyName} With this second option, we avoid mutating the original object by creating a new one that has the property. priceWithCurrency Immutability can actually be mandated by the language. JavaScript has the utility, but there are also mature libraries such as you can use instead. Nevertheless, before enforcing immutability everywhere, evaluate the tradeoff of adding a new library + the extra syntax; maybe you'd be better off creating an agreement in your team not to mutate objects if possible. Object.freeze Immutable.js 2. Function Composition It's the application of a function to the output of another function. Here's a small example: deductTaxes = grossSalary * addBonus = grossSalary + netSalary = addBonus(deductTaxes( )) const ( ) => grossSalary 0.8 const ( ) => grossSalary 500 const 2000 In practice, this means we can split out algorithms into smaller pieces, reuse them throughout our application, and test each part separately. 3. Deterministic Functions A function is deterministic if, given the same input, it returns the same output. For example: joinWithComma = names.join( ) .log(joinWithComma([ , ])) .log(joinWithComma([ , ])) const ( ) => names ', ' console "Shrek" "Donkey" // Prints Shrek, Donkey console "Shrek" "Donkey" // Prints Shrek, Donkey again! A common non-deterministic function is : Math.random .log( .random()) .log( .random()) console Math // Maybe we get 0.6924493472043922 console Math // Maybe we get 0.4146573369082662 Deterministic functions help your software's behavior be more predictable and make the chance of bugs lower. It's worth noting that we don't always want deterministic functions. For example, when we want to generate a new ID for a database row or get the current date in milliseconds, we need a new value to be returned on every call. 4. Pure functions A pure function is a function that is and . We already saw what deterministic means. A side effect is a modification of state outside the local environment of a function. deterministic has no side effects Let's look at a function with a nasty side-effect: sessionState = sessionIsActive = { (lastLogin > expirationDate) { sessionState = } } expirationDate = ( , , ) currentDate = () isActive = sessionIsActive(currentDate, expirationDate) (!isActive && sessionState === ) { logout() } let 'ACTIVE' const ( ) => lastLogin, expirationDate if // Modify state outside of this function 😟 'EXPIRED' return false return true const new Date 2020 10 01 const new Date const // This condition will always evaluate to false 🐛 if 'ACTIVE' As you can see, modifies a variable outside its scope, which causes problems for the function caller. sessionIsActive Now here's an alternative without side effects: sessionState = { (lastLogin > expirationDate) { } } { (currentState === && !isActive) { } currentState } expirationDate = ( , , ) currentDate = () isActive = sessionIsActive(currentDate, expirationDate) newState = getSessionState(sessionState, isActive) (!isActive && sessionState === ) { logout() } let 'ACTIVE' ( ) function sessionIsActive lastLogin, expirationDate if return false return true ( ) function getSessionState currentState, isActive if 'ACTIVE' return 'EXPIRED' return const new Date 2020 10 01 const new Date const const // Now, this function will only logout when necessary 😎 if 'ACTIVE' It's important to understand we don't want to eliminate all side effects since all programs need to do some sort of side-effect such as calling APIs or printing to some stdout. What we want is to minimize side effects, so our program's behavior is easier to predict and test. 5. High-order Functions Despite the intimidating name, high-order functions are just functions that either: take one or more functions as arguments, or returns a function as its output. Here's an example that takes a function as a parameter and also returns a function: simpleProfile = { { .log( ) longRunningTask() .log( ) } } calculateBigSum = { total = ( counter = ; counter < ; counter += ) { total += counter } total } runCalculationWithProfile = simpleProfile(calculateBigSum) runCalculationWithProfile() const ( ) => longRunningTask return => () console `Started running at: ` ${ ().getTime()} new Date console `Finished running at: ` ${ ().getTime()} new Date const => () let 0 for let 0 100000000 1 return const As you can see, we can do cool stuff, such as adding functionality around the execution of the original function. We'll see other uses of higher-order in curried functions. 6. Arity Arity is the number of arguments that a function takes. stringify = sum => x + y // This function has an arity of 1. Also called unary const => x `Current number is ` ${x} // This function has an arity of 2. Also called binary const ( ) => x, y That's why in programming, you sometimes hear operators such as or unary ++ ! 7. Curried Functions Curried functions are functions that take multiple parameters, only that one at a time (have an arity of one). They can be created in JavaScript via high-order functions. Here's a curried function with the ES6 arrow function syntax: generateGreeting = (relationship) => { .log( ) } greeter = generateGreeting( ) greeterCousin = greeter( ) cousins = [ , , ] cousins.forEach( { greeterCousin(cousin) }) greeterFriend = greeter( ) friends = [ , , ] friends.forEach( { greeterFriend(friend) }) const ( ) => ocassion ( ) => name console `My dear . Hope you have a great ` ${relationship} ${name} ${ocassion} const 'birthday' // Specialized greeter for cousin birthday const 'cousin' const 'Jamie' 'Tyrion' 'Cersei' ( ) => cousin /* Prints: My dear cousin Jamie. Hope you have a great birthday My dear cousin Tyrion. Hope you have a great birthday My dear cousin Cersei. Hope you have a great birthday */ // Specialized greeter for friends birthday const 'friend' const 'Ned' 'John' 'Rob' ( ) => friend /* Prints: My dear friend Ned. Hope you have a great birthday My dear friend John. Hope you have a great birthday My dear friend Rob. Hope you have a great birthday */ Great right? We were able to customize the functionality of our function by passing one argument at a time. More generally, curried functions are great for giving functions polymorphic behavior and to simplify their composition. 8. Functors Don't get intimidated by the name. Functors are just abstractions that wrap a value into a context and allows mapping over this value. Mapping means applying a function to a value to get another value. Here's how a very simple Functor looks like: Identity = ({ : Identity(fn(value)), : value }) const => value map => fn valueOf => () Why would you go over the trouble of creating a Functor instead of just applying a function? To facilitate function composition. Functors are agnostic of the type inside of them so you can apply transformation functions sequentially. Let's see an example: double = { x * } plusTen = { x + } num = doubledPlus10 = Identity(num) .map(double) .map(plusTen) .log(doubledPlus10.valueOf()) const ( ) => x return 2 const ( ) => x return 10 const 10 const console // Prints 30 This technique is very powerful because you can decompose your programs into smaller reusable pieces and test each one separately without a problem. In case you were wondering, JavaScript's object is also a Functor. Array 9. Monads A Monad is a Functor that also provides a operation. This structure helps to compose type lifting functions. We'll now explain each part of this definition step by step and why we might want to use it. flatMap What are type lifting functions? Type lifting functions are functions that wrap a value inside some context. Let's look at some examples: repeatTwice = [x, x] setWithSquared = (x ** ) // Here we lift x into an Array data structure and also repeat the value twice. const => x // Here we lift x into a Set data structure and also square it. const => x new Set 2 Type lifting functions can be quite common, so it makes sense we would want to compose them. What is a flat function? The function (also called join) is a function that extracts the value from some context. You can easily understand this operation with the help of JavaScript's function. flat Array.prototype.flat favouriteNumbers = [ , [ , ], ] .log(favouriteNumbers.flat()) // Notice the [2, 3] inside the following array. 2 and 3 are inside the context of an Array const 1 2 3 4 // JavaScript's Array.prototype.flat method will go over each of its element, and if the value is itself an array, its values will be extracted and concatenated with the outermost array. console // Will print [1, 2, 3, 4] What is a flatMap function? It's a function that first applies a mapping function (map), then removes the context around it (flat). Yeah, I know it's confusing that the operations are not applied in the same order as the method name implies. How are monads useful? Imagine we want to compose two type lifting functions that square and divide by two inside a context. Let's first try to use map and a very simple functor called Identity. Identity = ({ map: Identity.of(f(value)), : value }) Identity.of = Identity(value) squareIdentity = Identity.of(x ** ) divideByTwoIdentity = Identity.of(x / ) result = Identity( ) .map(squareIdentity) .map(divideByTwoIdentity) .valueOf() const => value // flatMap: f => f(value), => f valueOf => () // The `of` method is a common type lifting functions to create a Monad object. => value const => x 2 const => x 2 const 3 // 💣 This will fail because will receive an Identity.of(9) which cannot be divided by 2 We cannot just use the map function and need to first extract the values inside of the Identity. Here's where the flatMap function comes into place. Identity = ({ : f(value), : value }) ... const result = Identity( ) .flatMap(squareIdentity) .flatMap(divideByTwoIdentity) .valueOf() .log(result); const => value flatMap => f valueOf => () 3 console // Logs out 4.5 We are finally able to compose type lifting functions, thanks to monads. Conclusion I hope this article gives you a basic understanding of some fundamental concepts in functional programming and encourages you to dig deeper into this paradigm so you can write more reusable, maintainable, and easy-to-test software. Previously published at https://www.victorandcode.com/9-concepts-you-should-know-from-functional-programming