Functional Java: Understanding Pure Functions with JAVA

Reading Time: 3 minutes

Functional programming is a programming paradigm which is gaining its popularity day by day. It revolves around binding everything in pure mathematical functions style. It is is the process of building software by composing pure functions, avoiding shared state, mutable data, and side-effects.

Functional programming is declarative rather than imperative, and application state flows through pure functions. We call it as a declarative type of programming as the main focus is on “what to solve” rather than imperative style’s “how to solve”.

If you see, I have used the term “pure functions” here. So, what are pure functions? In this blog, I’ll try to answer this question.

What are pure functions?

A function is called as pure if it satisfies these two principles:

  • The return value of the function depends only on the input parameters passed to the function.
  • The execution of the function has no side effects.

Let’s try to understand these properties one by one. When we say that the return value of the function is dependent on the input parameters, we mean that:

  • We cannot have any outside variable state mutated inside a pure function.
  • For a particular input, we should get the same result, no matter how many times that function has been called. The output should always be the same for that input.

Consider the following example.

public class MathUtility {
    public static int sum(int num1, int num2) {
        return num1 + num2;
    }
}

In this sum() method, the resultant value is only dependent on num1 and num2. If we call sum(2,3), we will always get our output as 5. So the particular function can be called as a pure function.

Now, let’s talk about the second property of a pure function. When we say, execution of a function should not have any side effects, what do we exactly mean here?

public class MathUtility {
    private static int count = 1;
    public static int sum(int num1, int num2) {
        count++;
        multiply(num1,num2);
        return num1 + bnum2;
    }
}

In the above example, even though the method returns the same value for a given pair of input, we cannot call it as pure, because it does more than it advertises. The sum() function calls a method multiply() inside it. Also, it’s changing the state of the variable count, which is declared outside sum(). This is called a side effect. Here it violates the pure function second property.

JAVA 8 has come up with a functional style of coding. With the introduction of lambdas in it, can we say our lambdas are always pure? The answer is NO.
Let’s understand it with the example below.

public class MutateState {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Ajay", "Jaya", "Bruce");
        List<String> result = new ArrayList<>();
        names.stream()
                .filter(name -> name.length() == 4)
                .map(String::toUpperCase)
                .forEach(nameInUpperCase -> result.add(nameInUpperCase));
        System.out.println(result); //[AJAY, JAYA]
    }
}

In the main() method we create a list of names and an empty list to store the result. Then we iterate, using the stream(), through the list of names, pick only names that are four letters long, and transform them to uppercase. Toward’s the end, in the lambda expression we pass to forEach we receive a value of a name in uppercase and mutate the shared mutable list referenced by the result. This makes the lambda expression impure.

If we change the above stream() with parallelStream(), we may get unexpected results as code may run into a race condition due to the concurrent update of the result list by multiple threads and may lead to silent failures.
However, if we change the over stream code like this,

List<String> result =
        names.stream()
                .filter(name -> name.length() == 4)
                .map(String::toUpperCase)
                .collect(java.util.stream.Collectors.toList());

In this case, we are not mutating the state of any external variable. It is perfectly OK to change this to run in parallel if we see a need to do so, without the fear of race conditions and losing some data.

If we ignore the purity of lambdas, we may lose out on

  • The ability to easily parallelize the execution.
  • The results may be unpredictable due to lazy evaluation which is ingrained in the functional pipeline.

Functional programming emphasizes immutability and function purity. It’s on us to make the best out of it to make our code less prone to errors, easily testable and our life happy.

I hope, you have liked my blog. If you have any doubt or any suggestions to make please drop a comment. Thanks!

References:
The duality of pure functions

functional programming tutorials

Knoldus-Scala-Spark-Services

Written by 

Vinisha Sharma is a software consultant having more than 6 months of experience. She thrives in a fast pace environment and loves exploring new technologies. She has experience with the languages such as C, C++, Java, Scala and is currently working on Java 8. Her hobbies include sketching and dancing. She believes Optimism and a learning attitude is the key to achieve success in your life

Leave a Reply

Knoldus Pune Careers - Hiring Freshers

Get a head start on your career at Knoldus. Join us!