Functional programming has been around for a while, but it's really starting to gain traction. It's a different approach to writing programs instead of using object-oriented programming and it changes the way you think about problems and data. You focus less on how to do things and shift your focus to what things are. When you've been working in an object-oriented world, like most of us have, it can take a while to adjust to functional programming.
Although once you do adjust, it changes everything you thought you knew about a good program. You go from tracking down errors through crazy async calls where data types can change whenever they feel like it to methods that always return the same values when given the same data. Your code becomes close to bug-free and it's kind of crazy. We'll go over some background on functional programming, go through some examples in JavaScript, and then wrap up with some reasons you would want to use the functional approach.
Background on functional programming
The main goal of functional programming is to be able to reproduce values consistently, like you get with math equations. You want to know that when you put in data you will always get the right value back and functional programming accomplishes that. It uses a declarative approach to programming. Usually we describe the steps it takes to work with data instead of describing that data directly. Here's an example of the functional approach compared to the object-oriented approach.
Problem: get the total of a user's shopping cart
Object-oriented
Set the total variable to zero
put the price of each item into an array
Sum the prices in the array
Add taxes and shipping
Get total
Functional
The total of a user's shopping cart is the sum of the prices of all the items plus taxes and shipping
This is the core of the differences between functional programming and object-oriented programming. There are three main principles in functional programming that let us write code in this manner: immutability, separation of data and functions, and first class functions.
Immutability
Immutability handles complex issues like variable management. In object-oriented programming you normally assign values to variables and those values can change at any time. That can make it difficult to keep values in sync with the current state as your application grows to use thousands of variables. With all of those variables, it gets harder and harder to track down bugs.
Functional programming solves that problem by treating each variable like it is a value. It's not assigned a value, it is a value. For example say you have a user in your system and you want to give them new permissions. Normally you would do something like this.
let user = new User('contributor', ['view', 'write']);
user.addPermission('edit');
With functional programming you'll do something like this instead.
const user = {
role: 'contributor',
permissions: ['view', 'write']
};
const updatedUser = {
role: user.role,
permissions: […user.permissions].push('edit')
};
You'll notice that most variables are declared as const because of the immutability principle. This makes it so you can start with and keep an immutable initial data set, which means you have a definite single source of truth for your state. When you need to make changes to your data, you make a new variable that is that new value. That means every time you run through this code with that exact same data set you will get the exact same result.
Separation of data and functions
This is the trickiest part for people coming from an object-oriented background. In functional programming you have to keep your data separate from the code. No two-way binding allowed here. Instead of dealing with getters and setters and classes that reference other classes, you pass in the data you want your functions to work with. The data isn't included in the properties of a class where you have to manage the state of the properties.
You're working with a chain of constants that don't change the value of any data passed to it because of immutability. So if you're working with something like an array and you need to change a value, you make a copy of that array and make the updates to that. Here's an example of separation of data and functions in a simple budget tracker app in both the object-oriented way and the functional way.
Object-oriented
class PurchaseList {
constructor(purchases) {
this._purchases = purchases;
}
addPurchase(purchase) { /* do stuff */ };
}
class Purchase {
constructor(item, price, date) {
this._item = item;
this._price = price;
this._date = date;
}
getItem() {return this._item };
}
Functional
const purchase1 = {
item: 'toilet paper',
price: 12.47,
date: 2019-10-09
};
const purchase2 = {
item: 'plant food',
price: 10.87,
date: 2018-10-09
};
const purchaseList = [
purchase1,
purchase2
];
That's how data is separated from the functions from a code perspective. Functional programming deals mainly with arrays and objects in JavaScript, so make sure you are very familiar with the array and object methods.
First class functions
This is one of the more interesting parts of functional programming. You treat functions like any other data type. That means you can pass functions as parameters and return functions from other function calls. That brings up the concept of pure functions. A pure function is a function that doesn't depend on any state external to the function.
The only data a pure function has to worry about is the data that's being passed to it. When you have pure functions, the only way you will get a difference in the result is by passing in a different value. The returned result isn't effected by any data outside of the function. One of the goals of functional programming is to keep functions as pure as possible to avoid state management issues.
When the majority of your functions are pure, you can use those pure functions as "parameters" inside of other functions because you know that the pure functions are completely independent from everything else. We're going to make an example of a pure function and see how it's used when passed as a parameter.
Convert array example
To show how functional programming would work on something you might use, we'll go through an example by making a function that converts an array into something else. Say that you have an array full of unsorted, uncounted items for an e-commerce application. You want to return an object that has the name of each item and a count for each item to show a user. Here's how you would do that functionally.
const inventory = ['popsicle', 'underwear', 'sauce', 'pens', 'potatoes', 'sauce', 'onion', 'onion', 'pens', 'potatoes', 'ukulele', 'tomahawk', 'underwear', 'popsicle', 'sauce', 'ukulele', 'onion', 'underwear', 'popsicle', 'potatoes', 'onion', 'pens', 'ukulele'];
const countItems = inventory => {
return inventory.reduce((acc, name) => ({
…acc,
[name]: acc[name] ? acc[name] + 1 : 1
}), {});
};
What we've done here is made a function called countItems and it takes in an array called inventory. Then we use the reduce array method to turn this array into an object. Since the reduce method needs a starting point, we pass it an empty object as the second parameter for the method. Inside of the array, we use the spread operator on the acc variable to put the names and counts we have so far into the object we're returning.
Then we get the name that we are currently on in the array. We check the acc variable and if it doesn't have the current name in it yet, we'll initialize its count to 1. From there it goes through the entire array and keeps doing this check and count. There are a few things that make this pure function.
First you'll notice that there aren't any dependencies on external variables. Second, you'll notice that we used the spread operator instead of the actual acc variable. This keeps the immutability principle intact because we aren't changing the original variable. Lastly, the data is completely separate from the function. If you pass in the initial array multiple times, you'll always get the same result without worries and the same stands for any other array you pass in.
Reasons to use functional programming
Functional programming is a different approach to programming than object-oriented programming and it helps with a lot of problems OOP has. For starters, it helps you prevent almost all bugs and it makes your code way more readable. Because the results from your functions are always the same, it makes your applications more maintainable, dependable, and scalable overall. Another thing you don't have to worry about as much is state management because none of your functions or variables are as heavily dependent on state as they would be in OOP.
The way you have to think in the functional programming paradigm takes some getting used to if you come from an OOP background. Once you get used to it though, it'll be hard to go back to OOP because you notice all of the issues that functional programming solve. Your code is cleaner and it's just refreshing to not have unexpected changes in data.
What do you think? I really like the functional approach, especially for production applications. Have you had any experience, good or bad, with functional programming?
Hey! You should follow me on Twitter because reasons: https://twitter.com/FlippedCoding
Top comments (20)
This is a really great intro post to Functional Programming for people that are not used to it. It's also good that you used JS to explain it!
Functional programming is love and is life :D (coincidentally I once gave a talk with that title hahaha)
Starting from very first example you're mixed up object oriented and imperative programming. In fact there is no contradiction between object oriented and functional programming. Object oriented code can (and often do) use immutable data and pure functions. Object oriented code is more about how code and data organized together than about how code does things.
Is there such a thing as hybrid programming? Mixing both styles?
When we programs in a modern C# or Kotlin we definitely mix functional and object-oriented programming. Scala was specifically designed to combine improved Java OOP with functional programming. JavaScript supports both... well, in JavaScript there is virtually no limits to what you can do.
In any modern language there's no limits to what you can do.. JavaScript only has the support it does because it's there by default in web browsers. I, for one, am happy with our upcoming wasm overlords.
Yes, WASM looks intriguing.
Uncle Bob says that the best software is a mix of all paradigms.
Structured programming - this is the basis of our algorithms.
Functional programming - how we push data to the boundaries of our applications and elegantly handle program flow.
Object-Oriented programming - how we define relationships between modules / how we cross architectural boundaries with polymorphism and plugins
This is a really great summary. Quick question - couldn't objects defined using const still be mutable in a sense if I set a property on it?
Correct.
const
in javascript does not mean immutable. It means the identifier can't be re-assigned.This might be the first time that it's made sense in a way that I can actually apply it to real-World scenarios.
Thanks Milecia
I'd love to see some functional programming patterns that deal with async operations, such as networking.
The short answer to this is that those type of operations (side effects), are dealt with by pushing them out to the edges of your code, by extracting them from the core logic of your program and evaluating them lazily.
An example of this is making a network request for some data which you then have to do some processing on. We can return something like a Promise which allows us to define the processing we want to do with whatever the result might be, and only execute that Promise at a later time.
Unfortunately the short answer still leaves a lot of questions which are best answered by a more in depth look at topics like Option/Maybe types. I've just started a series on basic functional topics and hopefully I'll eventually get to covering more advanced topics too.
Promises, probably, one of the most convenient patterns for async code. And they are FP as well.
I'm still reading but this tripped me up: "shift more to figuring how what things are." I think you a word.
Oof... I thought I caught everything, but definitely missed this. Thanks! I'll update that.
Great article and super informative! Thanks for posting!
Do you use functional programming whenever possible? Or is there a situation where you might choose OOP even though you could've used functional?
Pick the one that can be easily tested and is most easily understood when you read it.
Thanks Milecia. I always look forward to your posts!
Thank you!
how to create validated value objects and invariants in functional programming?