Immutability in Functional Programming
The literal meaning of Immutability is unable to change. In the Functional
Programming world, we create values or objects
by initializing them. Then we use them, but we do not change their values or their
state.
If we need, we create a new one, but we do not modify the existing object's state.
Let
me give you some examples in Scala.
Scala has two types of variables.
- var
- val
The var stands for a variable, and the val stands for value. You can initialize a var, and later you can reassign a new value to the var.
The next one is the val. The val is a constant. That means, once initialized, You cannot change it.
So, reassignment to val is an error. You cannot do it. Since Scala is a hybrid language, It supports var and val both. However, when you are using Scala as a Function Programming language, It recommends using val instead of var. Using val will force you to create programs using only the constants. You might be wondering with below question.
Why do we program with Immutables?
That is a valid question. How can we program with only constants? I mean, without changing a value of the variable, we cannot even create a simple loop. Using only constants appears to be the biggest limitation. More importantly, the real world is not immutable. Whatever you are trying to model using your program will have the change in its state. Moreover, above all of that even if we try to do it, I wonder what the benefits are? Why do we want to adopt the limitation of immutables? We have just two questions behind all such arguments.
- How can we program without a variable?
- What are the benefits of using Immutables?
At this stage, I can give you two advantages.
It helps us to adopt a mathematical approach
The Functional Programming has its origin in the mathematical functions. In a real world, objects do change their state, but in mathematics, objects do not change their state. Let's consider a simple mathematical expression.
Both of the above expressions are same. Isn't it? When we want to apply a sum function, we pass two integer objects, and the function returns a new integer object. The function does not change the value of the input parameters. It returns a new object by adding the two input values. The Functional Programming is more inclined towards creating Functions in a mathematical sense. The immutability helps us to take a mathematical approach and create pure functions.
Immutability helps us to avoid various problems
The immutability is a big thing in a multithreaded application. It allows a thread
to act on an immutable object without
worrying about the other threads because it knows that no one is modifying the
object.
So the immutable objects are more thread safe than the mutable objects. If you are
into
concurrent programming, you know that the immutability makes your life simple.
However, a more interesting example comes from the data pipelines. We often use
immutability
in data engineering process. Let's try to understand it with a hypothetical
scenario.
You have a dataset. You are requested to perform a data quality operation. You want
to
achieve two things.
- Remove the records where the date column is blank.
- Change the date format of all rows into a consistent format and time zone.
You collected sales transactions from your retail stores all over the world. Some
of them do not even have a sales date.
You do not want to consider them for your analysis. Since they came from different
countries
and different systems, they have different date and time zone formats. You may want
to
convert all of them into a single format. How will you do it?
Will you delete all records with a blank date?
Will you update all rows to a consistent date format?
Assume you did that and a few days later, you identified a bug in your code.
You
can always fix your code and redeploy it but what about your data? You modified
original
data, and you lost the original values. You deleted some records as well. The bug
in
your code has corrupted your data. It is no longer reliable, and now, you have no
way
to fix your data problem.
The better option is to take the immutable approach and do not modify the original
data set. Write one function that creates
a new data set T1 by filtering out all records
with a blank date. Then a second function to convert the date field into a
consistent
format and save it as new dataset T2. When you identify a bug in your
code,
You just execute the new function on the original and immutable data set. The point
that
I want to make is that the immutable approach helps us to simplify the solution and
avoid
many problems. You can even avoid some human errors.
Now, let's come back to the next question.
How can we program without a variable?
It looks like a difficult thing but not impossible in many cases, and the functional programming languages, like Scala, provide you enough language constructs to achieve that. Let me show you a simple loop example, and a solution to achieve immutability.
The above function takes a positive number n and returns a factorial value. I am using two vars in this function. Now, I have two questions for you.
- Is this a pure function?
- Can you rewrite this function without using vars?
I leave the first question for you to answer. The second one is simple. If you learned any modern programming language, you must have learned recursion. A Recursion is a programming technique in which a function calls itself. You can replace most of the loops with a recursion. We use recursion extensively in functional programming. I know, recursion is one of the toughest things to use. However, with some practice, you should be able to use it as a powerful tool. Let me convert the factorial function to a recursive solution.
This one example of converting a loop into a recursion may give you an assurance
that you can replace your loops with a recursion
and avoid using vars. However, let me ask
you a straight question.
Are you convinced that you can program without using vars?
How can we program without a variable?
This question is still standing tall enough. However, this limitation should
not
be a problem because you can create new value for each new state. We used the same
trick
in our data quality check example. Instead of changing the original data set, we
created
two new tables. You can take the same approach and create new values instead of
changing
existing ones. Moreover, we are not taking an oath as a functional programmer to
keep
everything immutable. Mutability may have its definite advantages, and we are free
to
choose what suits best for the given problem. The point is that the immutability is
a
powerful thing. It simplifies the solution and avoids many problems, so it is the
default
approach in Functional Programming. A Functional programmer challenges every
mutation
and tries an immutable alternative.
Continue reading for more functional concepts.
Read More
Pure Functions | Referential Transparency | Benefits of pure functions | First class functions | Higher order function | Anonymous functions | Immutability | Tail Recursion | Expressions in Scala | Lazy Evaluations | Pattern Matching | Closures