Functional Programming for Java Developers, Part 4 - Immutability



One important concept in functional programming is - variables are immutable. Technically speaking, purely functional programming languages don't have variables. For example, if you write x = 1 in Haskell, you're saying that x is 1. You don't say that x stores the value 1 currently, and wish it storing something else later. What you can do is to get the value 1 through the name x.

Are there problems if variables are mutable? If variables are mutable in a code flow, changing their values is too easy to divide a problem into sub problems. A method using non-local variables may have side effects. That is, the method may return different results even if you give the same arguments. The method may have invisible inputs and outputs. If the state of an object is mutable, the object may be the aggregation of side effects, because field members are non-local variables for methods. The difficulty of tracing variables will increase to the level of tracing the state of an object. Once the object is used in a concurrent context, it's hard to deal with synchronization of the object state.

Immutability is a basic feature in purely functional programming languages. It forces us to decouple a long code flow into smaller sub flows. A method using non-local variables has no side effects. An object is not the aggregation of side effects. There's no problem about thread synchronization.

It may be hard for you – an imperative programmer - to think about immutable variables, but it's not so hard. Have you found out that we didn't change any variable value or object state in those examples of previous articles? We map the head element and pass the tail list to the map method. We filter the head element and pass the tail list to the filter method. We reduce the head element and pass the tail list to the reduce method. We didn't change any variable value or object state.

Once you cannot use mutable variables, control flows will be changed.

For example, you cannot use loops, such as for or while. The problem of loops is that they are born to produce side effects. They always change variable values or object states. Changing several variable values or object states in a loop is easy for programmers and produces more and more complex code flows. That is, the loop may deal with several sub problems at the same time and make the loop itself a logic clump.

Let's think a question. Why do programmers use loops? They want to solve repetitive problems. What do repetitive problems mean? Each repetitive problem is basically a sub problem. What's the substitution if programmers cannot use a loop to solve a sub problem? Yes, recursion. But, as we've seen in Functional Programming for Java Developers, Part 1, the point is not recursion. The point is how to divide a problem into sub problems. Yes, the point is not loops. The point is how to divide a problem into sub problems. Loops and recursion are only tools to describe sub problems. But, recursion is the better, clearer and preferred tool to do that.

We can see that. Once variables are immutable, you're forced to divide a problem into sub problems because you have no choice. Immutability is a way to force you to find a logical clump of code and use a method to extract that. So, you cannot use a loop, recursion is the better substitution. You have to use a method to encapsulate an if-else statement because an if-else statement is also born to modify variables. For example, if you have the following code...

String nickName = getNickName("Justin");
if(nickName == null) {
    nickName = "Guest";
}

You may define a getOrElse method to encapsulate if statement.

static String getOrElse(String original, String replacement) {
    if(original == null) {
        return replacement;
    } else {
        return original;
    }
}

Then, you can use the getOrElse method to avoid modifying the variable.

String nickName = getOrElse(getNickName("Justin"), "Guest");

In fact, Java has a similar syntax to the if-else expression commonly used in a functional programming language. That is the ternary operator ?: - using it is not suggested though. If you really want to use the ternary operator, you may write code as follows:

String name = getNickName("Justin");
String nickName = (name != null ? name : "Guest");

We've seen the points from the first chapter "Why Function Programming?" of the book Functional Programming for Java Developers in Functional Programming for Java Developers, Part 1. Because we've known what algebraic data Types, list patterns and immutability are, we can review and explain those points briefly now.

  • I Have to Be Good at Writing Concurrent Programs
Functional programming has no side effect because of immutability.
  • Most Programs Are Just Data Management Problems
Functional programming defines and uses algebraic data types. Algebraic data types are easier to use in the problem with regularity, such as data management problems.
  • Functional Programming Is More Modular
Before doing functional programming, you have to divide a problem into sub problems. Once you have solutions for sub problems, you may use these solutions in other problems with the same sub problems.
  • I Have to Work Faster and Faster
Your code will become clearer. You'll find more high level abstractions. You'll have more solutions for common problems. You'll work faster and faster.
  • Functional Programming Is a Return to Simplicity
Once you are familiar with functional programming, what you have to do is dividing a problem into sub problems. That's almost the all story.
   
But, as we've seen in Functional Programming for Java Developers, Part 1, you cannot just apply all concepts directly without any thinking if your language is not a functional language, such as Java. In the next article, we'll get back to the real Java and see what we should take from functional programming.