Using Functional Programming in JavaScript
Using Functional Programming in JavaScript
I've been thinking more about functional programming in JavaScript and why some of its ideas are useful, especially as JavaScript applications become more complex.
I'm not talking about turning everything into Haskell or trying to write JavaScript in a completely different style. There are a few functional programming concepts that seem especially useful for writing code that's easier to reason about and potentially easier to scale.
Why Functional Programming?
No Side Effects
One of the biggest ideas is avoiding side effects whenever possible.
Given the same input, a function should ideally return the same output without modifying anything outside of itself.
Instead of:
let total = 0;
function add(value) {
total += value;
}
you can do:
function add(total, value) {
return total + value;
}
The second version is much easier to understand.
You don't have to figure out what happened to total somewhere else in the application. Everything the function needs is passed into it, and everything it changes comes back as a return value.
That becomes increasingly important as an application grows.
Less State
State makes applications harder to reason about.
The more shared mutable state you have, the more questions you have to answer:
- Who changed this value?
- When did it change?
- What depends on it?
- Can two things change it at the same time?
Reducing state doesn't mean an application literally has no state. Obviously applications need to remember things.
The goal is more about keeping state controlled and minimizing the number of places that can mutate it.
const updatedUser = {
...user,
name: "Taku"
};
Instead of modifying the original object, we're creating a new one.
That makes the flow of data much more predictable.
Getting Ready for Concurrency
Another reason I find functional programming interesting is concurrency.
JavaScript in the browser is traditionally single-threaded, but that doesn't mean concurrency doesn't matter. We already deal with asynchronous operations constantly:
Promise.all([
getUsers(),
getProducts(),
getOrders()
]);
As applications become more concurrent, shared mutable state becomes increasingly dangerous.
If multiple operations depend on or modify the same state, it's very easy to create race conditions and unpredictable behavior.
Pure functions don't have this problem because they don't depend on shared state.
function calculateTotal(items) {
return items.reduce((total, item) => {
return total + item.price;
}, 0);
}
You can run this function whenever you want without worrying about what else the application is doing.
That seems like an important property as JavaScript continues moving toward more parallel and concurrent workloads.
Easier Testing
Pure functions are also extremely easy to test.
function multiply(a, b) {
return a * b;
}
Testing this doesn't require a database, browser, global variable, or complicated setup.
multiply(2, 3) === 6;
When functions don't depend on external state, tests become much simpler.
This is probably one of the most immediately useful benefits of functional programming.
Easier to Reuse Between the Frontend and Backend
There's also an interesting relationship between functional programming and isomorphic JavaScript.
If the same JavaScript code can run on both the frontend and backend, that code can't depend too heavily on the environment around it.
For example:
function calculateDiscount(price, percentage) {
return price - price * percentage;
}
There's nothing browser-specific or server-specific about this function.
The frontend can use it.
The backend can use it.
Tests can use it.
That doesn't mean the frontend has no state. It means the business logic itself doesn't have to care where it's running.
I think this is where functional programming becomes especially useful for isomorphic applications.
The more logic that can exist as simple functions, the easier it is to share that logic across environments.
Composition
Another concept I like is building larger functionality by combining smaller functions.
function double(value) {
return value * 2;
}
function addTen(value) {
return value + 10;
}
function calculate(value) {
return addTen(double(value));
}
Each function does one thing.
That makes each piece easier to test, reuse, and understand.
Instead of having one huge function that modifies a bunch of state, you can build behavior out of small predictable functions.
JavaScript Is Already Pretty Functional
What's interesting is that JavaScript already has a lot of functional programming concepts built into it.
Methods like:
map()
filter()
reduce()
encourage you to transform data instead of manually modifying it.
For example:
const activeUsers = users
.filter(user => user.active)
.map(user => user.name);
There's very little state involved here.
We're basically saying:
- Take these users.
- Keep the active ones.
- Return their names.
That feels much easier to reason about than manually managing counters, temporary arrays, and mutable variables.
Conclusion
I don't think JavaScript applications need to be 100% functional.
Applications have state. They perform I/O. They talk to APIs and databases. Side effects are unavoidable.
But functional programming gives us some useful principles:
- Minimize shared mutable state.
- Prefer pure functions when possible.
- Make data transformations explicit.
- Build larger behavior from smaller functions.
- Keep business logic independent from the environment where possible.
The more predictable each individual piece of an application is, the easier the entire application becomes to reason about.
And as JavaScript applications become more distributed, asynchronous, and potentially more concurrent, I think these ideas become increasingly valuable.