Going Functional With Kotlin — Part 1: What is functional programming?
--
Twitter | LinkedIn | YouTube | Instagram
This story is part of a series.
Next: Going Functional With Kotlin — Part 2: Higher-Order Functions
One of the fascinating aspects of Kotlin that sets it apart from many other languages is its support for functional programming. This makes it the perfect language to get started. You can keep following a more imperative paradigm while you start applying a more functional style as you feel comfortable.
And if you’re unfamiliar with functional programming, don’t worry because so am I. In this series of stories, we will learn together from a beginner's perspective. We'll start from the basics and gradually build up to more complex concepts. By the end of this series, we'll have a solid understanding of how to write functional code in Kotlin and why we would want to do so.
What is functional programming?
Before we go into the details of functional programming, let’s get back to school and remember what a function is in mathematics.
If you remember correctly, a function is defined as:
If you input three into the function above, it will output 5.
Functional programming isn’t a novel concept. Its roots trace back to the 1930s with the invention of lambda calculus, a foundation of mathematical computation. This connection to mathematics isn’t just historical trivia; it’s the essence of functional programming.
Cool! But why do I care?
In functional programming, your code is made up of functions that work a lot like functions in math. A function takes some input and produces an output.
Let’s implement the previous function in Kotlin:
If you run the code above by pressing the play button, you will see that 5 will show up in the console. Experiment changing the input, and you will see that the output is always the input plus two.
And even though this may be basic, it is an important lesson of functional programming. Because in functional programming, every function should be pure.
What is a pure function?
A pure function is one that doesn’t have any side effects — it doesn’t change any state or modify data outside of the function. It only takes inputs and produces an output, and given the same input, it will always produce the same output.
Look at the example below. The incrementCounter
is an impure function because it modifies the global variable counter
. The function’s output depends on the state of counter
, and it changes the state of counter
each time it’s called.
This means that calling the function with the same input doesn’t always produce the same output, which is a key characteristic of impure functions.
How would you convert the function below into a pure function? I challenge you to do it and send the answer in the comments.
With pure functions, debugging becomes simpler. Because functions are pure, you can be confident in their outcomes, making testing a breeze.
Also, in an era where parallel processing is becoming the norm, FP shines with its stateless functions, leading to fewer bugs in concurrent applications.
And the concept of pure functions goes hand-in-hand with the principle of immutability.
Why should your data be immutable?
Everyone tells you your variables should be immutable and that you should always opt for val
instead of var
. But why is that?
As discussed in the previous sections, pure functions are operations that don’t alter any state; they simply process input and generate output without producing any side effects.
And when your data is immutable, meaning it can’t be changed after it’s created, it simplifies the process of writing pure functions. This is because you can be confident that the data your function is working with will remain constant, eliminating the potential for unexpected side effects.
In our previous example, you saw that the functions not only were not pure as the counter was not immutable. How can we refactor that code in order to make the function pure and the counter immutable?
To do it, we must ensure that the function does not modify any state outside of its own scope. We can achieve this by passing the current state of the counter as a parameter to the function. In the example below, we’re making the function pure, and we are also not relying on any mutable variables anymore.
At this point, we already understand that functional programming languages treat functions as first-class citizens. This means that functions can be passed as arguments to other functions, returned as values from other functions, and assigned to variables.
This leads us to the concept of higher-order functions, which are functions that operate on other functions, either by taking them as arguments or by returning them as a result. Let’s learn more about them in the next part of this journey! Stay tuned!
Medium is changing (Did you know?)
Medium Partner Program (The Monetization Program) has suffered great changes since the 1st of August, 2023. Before that date, Medium would pay writers based on the time readers spent on their stories.
Unfortunately, this has changed. Medium pays writers now based on the interaction with their stories. The amount they earn is based on claps, follows, highlights, and replies. I really hate to do it, but if you enjoyed this story and other stories on Medium, don't forget to interact with them. This is an easy way to keep supporting the authors you like and keep them on the platform.
Contribute
Writing takes time and effort. I love writing and sharing knowledge, but I also have bills to pay. If you like my work, please, consider donating through Buy Me a Coffee: https://www.buymeacoffee.com/RaphaelDeLio
Or by sending me BitCoin: 1HjG7pmghg3Z8RATH4aiUWr156BGafJ6Zw
Follow Me on Social Media
Stay connected and dive deeper into the world of Kotlin with me! Follow my journey across all major social platforms for exclusive content, tips, and discussions.