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.

  1. var
  2. 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.

                                        
    var s = "Hello World!"
    //After initializing it once, you can change it later.
    s = "Hello Scala!"                
                                    

The next one is the val. The val is a constant. That means, once initialized, You cannot change it.

                                        
    val v = "You cannot change me."
    v = "Let me try."
    //Output:- <console>:8: error: reassignment to val                 
                                    

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.

  1. How can we program without a variable?
  2. 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.


                                        
    3 + 1 = 4
    //Let me rewrite the above expression in a different notation. 
    sum(3, 1) //returns 4                 
                                    

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.

  1. Remove the records where the date column is blank.
  2. 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.

Immutable approach to data pipeline
Fig.2-Immutable Approach in data pipelines

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.

                                        
    def iFactorial(n:Int):Int = {
        var i=n
        var f=1
        while (i>0) {
        f = f * i
        i = i -1
        }
        return f
        }                 
                                    

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.

  1. Is this a pure function?
  2. 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.

                                        
    def rFactorial(n: Int): Int = {
        if (n <= 0) 
            return 1
        else
            return n * rFactorial(n-1)
        }                    
                                    

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

By Prashant Pandey -


You will also like:


Kafka Core Concepts

Learn Apache Kafka core concepts and build a solid foundation on Apache Kafka.

Learning Journal

Pattern Matching

Scala takes the credit to bring pattern matching to the center.

Learning Journal

Apache Spark Introduction

What is Apache Spark and how it works? Learn Spark Architecture.

Learning Journal

Scala placeholder syntax

What is a scala placeholder syntax and why do we need it? Learn from experts.

Learning Journal

Higher Order functions

Scala allows you to create Higher Order functions as first class citizens.

Learning Journal