What is functional programming?
Functional programming (FP) is a coding paradigm which, what may come as a surprise, has its roots in mathematics. But fear not - we will not dwell on the mathematical aspects in this article (or future parts of the series). For now, let’s focus on the core principles that FP relies on:
- function purity,
- avoiding side effects,
- avoiding shared state,
The short list above should be enough to see that it differs greatly from the more traditional approaches like procedural programming or object-oriented programming. Why is it so and what are the benefits? Let’s dive a bit deeper.
Sidenote: This topic may seem scary at times and certainly isn’t beginner-friendly. If you ever feel lost, don’t worry! It may take some getting used to, but don’t get discouraged easily.In fact, you have probably used some of the functional concepts in practice, without even knowing it. And if you’re just at the beginning of your developer journey, check our tips about how to start a career as a frontend developer and what should be your first steps to become a Junior Web Developer.
Function purity and avoiding side effects
You’ve probably heard about pure functions all over the place. So what are they? A pure function is a function that produces no side effects. In simple words - it does not modify or rely on any variable outside of its scope, nor does it produce any output beside the value it returns (for example: no logging to the console or making requests). A pure function, given the same inputs, will always return the same output, no matter the context or application state.
Referential transparency is the assertion that a function call could be replaced by its output value, and the overall program behavior wouldn't change. In other words, it would be impossible to tell from the program's execution whether the function call was made or its return value was inlined in place of the function call.
Avoiding side effects by using pure functions wherever possible makes our code easier to reason about - we don’t need to worry about the context at all! The result of any function depends solely on the arguments passed in. Needless to say, it makes writing tests a lot easier too.
Avoiding shared state
This may seem counterintuitive to developers used to the object-oriented approach, but functional programmers will try to avoid using any shared state whenever possible. Instead, we will try to rely on deriving and calculating new data every time.
But why? What’s the problem with shared state?
The main downside to using any form of shared state is the effort we need to put in in order to fully understand the effects of any function call. To predict the outcome and next state value, we would need to know about all previous function calls that modified any of the shared variables. That seems like a lot of work, doesn’t it? By getting rid of shared state, we can avoid a lot of unnecessary mind work and hard to catch bugs.
In simple words, immutability means not making any changes to an object after it has been created. Functional programming greatly encourages us to mutate data as little as possible. Instead, we should always derive new data from the old, for example by copying it into another variable. Although it seems straightforward at first glance, it may be tricky to avoid mutating values at times. We will dig deeper into the common gotchas and why it’s so important in a future part of the series.
Is it worth it - pros and cons of Functional Programming?
Functional programming takes some time getting used to. Some of its concepts (like recursion) require a change in the way we think about our code, and thus can be problematic. The terminology used in FP may be intimidating at times, too. These things tend to discourage many developers, especially at the beginning.
Also, avoiding mutations can sometimes negatively affect performance, for example by copying data instead of mutating it in place, thus using more memory.
The most important advantage is the understandability of the functional code. By focusing on being declarative, we signal to the user what we want to do, instead of just telling the computer how to do it.
What’s more, when knowing that a function is pure, we can feel safer when using it, and do not need to worry about potential unexpected bugs it could cause in another part of the program. It makes the code much more testable and maintainable.
The performance issues mentioned above may never even be visible by a human eye (and probably never will in most applications you will write). Of course, if you encounter any performance-related problems, consider switching to another paradigm. You don’t have to write fully functional code everywhere :)
Another advantage may be the fact that more and more JS tools are adopting functional programming. If you’re familiar with React, you probably heard of higher order components. Another widely known example would be Redux - its core principles align very well with FP principles we’ve just talked about. For example, it requires using pure reducers and immutability, and encourages the use of concepts like composition and higher order functions. You may have also heard of RxJS, a functional reactive programming library worth its own article series.
Wrapping it up
Our brief introduction into the topic of functional programming has come to an end.
In the future parts of the series, we will get to know the basics of some FP concepts in practice. There will finally be some code! We will be using some of the newest ES6+ features (for example arrow functions and rest/spread), so if you’re not familiar with them already, be sure to get up to speed (for example by reading the "ES6 & Beyond" part of “You Don’t Know JS” series). What’s more, we will make frequent use of closures, higher order functions and array operations like map or reduce (assuming you’re familiar with them, so catch up if you’re not :)). It will start to get more and more exciting from now on, so stay tuned for the next part!