Functional programming is not a new paradigm. According to Wikipedia, the idea started popping up in the 1930s. The main concept behind it is to compose functions in a chain.
Unlike the imperative way of programming where developers have to store the state, functional programming is stateless. All computations and data manipulations happen in functions without keeping the intermediate state of the data. You can think of it as a pipe: Each piece is a function that receives some data and returns the result. That result becomes an input for another function.
The functional programming concept is powerful and has gotten the attention of many developers. Many modern programming languages support it. There are even pure functional languages like Clojure, Haskell, and F#.
But what makes this paradigm that favourable? If it only has advantages, why hasn’t it replaced the imperative paradigm yet?
The Good Part
Functional programming gets its traction when it comes to filtering data and changing the format. If solutions require filtering out some data, changing the order, or picking only certain pieces of data, then there is a good chance you are on the right path. Let’s explore the following example:
|
We have a hash where companies are keys and cities are values. We would like to get the cities where tech giants like Apple, Microsoft, and Amazon are located. For that, we filter the hash to select only tech giant companies, then we fetch the cities and remove any duplicates.
The best part of functional programming is that it makes code simple and readable. So we get our result in only five lines of code (lines 11-15). How much code would it take with an imperative approach?
We would need to have a loop through all the items. In that loop, we would have to use an if statement to check that a certain item is a proper fit. Along with all of that, we would introduce a few temporary variables to save the intermediate state. When you implement it, it would definitely be more than five lines of code.
Writing code in a functional way can be addictive, as it simplifies many things. This concept has gained popularity in recent years. Many modern languages like Kotlin, Ruby, and Swift use it extensively.
The Not So Good Part
Along with simple functions like filter and map, there are advanced functions. Chaining these into a long sequence can reduce code readability in some cases. At that point, the benefit of compact code becomes a problem.
total_duration = events.pluck(:duration).compact.sum.round
Check out the example above. Chaining together four different functions is not that difficult, but the issue with it is that you have to know what each function does. Without knowing this, debugging this line can take some time.
This example is in Ruby, and it does the following: It picks the duration attribute from the collection of events. The compact function removes all nullable items. The sum and round functions are simpler: They calculate the sum of the duration and round the final result to the nearest integer.
Every programming language introduces specific functions for processing data structures. To name a few of them: reduce(), collect(), andÂ
fold(). In combination with other functions, that can lead to difficulties in understanding. Imagine chaining together over ten functions, including advanced ones. I doubt your teammates would be happy dealing with that code.
Selecting necessary functions carefully can save a lot of time for you and your team.
The Pitfalls
There is also something else you must be aware of when using functions: The order of functions is important. If you have to do filtering and mapping on an array, then (if possible) filtering should be done first and then another operation because that next operation will be processing an already-reduced dataset.
scores = [2.1, 4.2, 6.4, 1.3, 5.6, 7.1, 2.3]# sorting 3 elements
scores.select{|score| score > 5.0}.sort
Â
# sorting 7 elements
scores.sort.select{|score| score > 5.0}
When operations are in the opposite order, it wastes computational resources for processing data that will be excluded later. In the second example, the sorting operation is performed on seven elements. In the first example, it is performed on only three elements. Obviously, the difference in performance will be visible on bigger datasets.
Final Thoughts
Functional programming is not a silver bullet. Software engineers cannot start writing complex systems by solely following that paradigm. At this point, you might think about pure functional languages such as Clojure or F#. Of course, those languages were meant to be used only for a functional way of thinking.
It all comes down to your needs. If your application is supposed to be stateless, then functional programming is the way to go. But if your solution is stateful and requires state management, applying a functional approach will look unnatural.
In the end, it is another great tool for solving specific problems. Picking up the right instrument to achieve your goal is an important skill — and the functional concept is one of them.