What are Pure & Impure Functions?
This lesson details pure and impure functions with various examples for understanding.
Table of Contents
Introduction
Understanding the difference between pure and impure functions is important in functional programming, where the emphasis is on writing code that is easy to reason about and test.
The main differences between pure and impure functions are:
- Pure functions take objects or primitive data types as arguments but do not modify the objects. They don’t have side effects.
- Impure functions change the state of received objects.
Pure functions
A pure function is a function that has no side effects and always returns the same output for the same input parameters. In other words, a pure function is completely deterministic and does not modify any external state.
Functions are not called pure when they change in state internally or externally. Following are some examples of impure functions:
Add numbers
The following example snippet calculates the sum of two inputs and returns the output. If the inputs are the same for both values, then the output is always the same.
class Sum {
public static void main(String[] args) {
int sum = addNumbers(10, 9);
System.out.println(sum);
}
public static int addNumbers(int first, int second) {
return first + second;
}
}
No matter how often we run the above method/function with the same inputs, we get the same output.
Test Cases: The following couple of test cases explain these functions' functionality.
- If we give
10
and9
as input, we get19
as output. - For inputs
22
and5
, we get27
as output always.
String manipulation
The String
class provides functions to manipulate strings. Since these functions do not modify the input string and always return a new string based on the input parameters, they are pure functions.
public class StringManipulation {
public static void main(String[] args) {
String first = "Hello";
String second = "World";
String result = add(first, second);
System.out.println(result);
}
public static String add(String s1, String s2) {
return s1.concat(s2);
}
}
Math functions
The Math
class provides functions to perform mathematical operations. Since these functions always return the same output for the same input parameters and do not modify any internal state, they are pure functions.
public class MathFunctions {
public static void main(String[] args) {
int a = 2;
int b = 3;
double result = mathOperation(a, b);
System.out.println(result);
}
public static double mathOperation(int a, int b) {
return Math.pow(a, b);
}
}
Immutable data structures
The java.util
package provides immutable data structures such as List
, Set
, and Map
. Since these data structures cannot be modified after creation, functions that operate on them are pure.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamOfIntegers {
public static void main(String[] args) {
List<Integer> input = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> result = streamOperation(input);
result.forEach(System.out::println);
}
public static List<Integer> streamOperation(List<Integer> input) {
List<Integer> multiplyBy2 =
input // collection of integers
.stream() // Stream of integers
.map(n -> n * 2)
.collect(Collectors.toList());
return multiplyBy2;
}
}
Functional interfaces
Java 8 introduced functional interfaces such as Predicate
, Function
, and Supplier
that represent pure functions. Since these interfaces only take input parameters and return output based on those parameters, they are pure functions.
import java.util.function.Predicate;
public class FunctionalInterfaceEx {
public static void main(String[] args) {
int A = 4;
boolean result = isEven(A);
System.out.println(result);
}
public static boolean isEven(int A) {
Predicate<Integer> isEven = n -> n % 2 == 0;
return isEven.test(A);
}
}
Generally, any function that only depends on its input parameters and does not modify any external state is pure.
Impure functions
An impure function is a function that can have side effects or whose return value depends on some state other than its input parameters.
- Impure functions can modify the program's state or depend on some external state, making them less predictable and potentially harder to reason.
- Impure function mutates the state.
In most applications, an object's data is changed using a setter injection. This happens in an application with massive variables and objects constantly talking to each other, and at some point, the object reaches a state where it's hard to define the state of that object.
Following are some examples of impure functions:
Random number generator
The Math.random()
function generates a random number between 0.0
and 1.0
. Since the value returned by this function depends on the internal state of the random number generator, it is an impure function.
public class RandomNumber {
public static void main(String[] args) {
System.out.println(generateRandomNumber());
}
public static double generateRandomNumber() {
return Math.random();
}
}
Date/time functions
The java.util.Date
and java.time.LocalDateTime
classes provide functions to get the current date and time.
Since the value returned by these functions depends on the current system time, they are impure.
import java.time.LocalDateTime;
import java.util.Date;
public class DateTimeFunctions {
public static void main(String[] args) {
printDateTime();
}
public static void printDateTime() {
Date currentDate = new Date();
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current date is: " + currentDate + ", and time is: " + currentDateTime);
}
}
File I/O operations
The java.io
package provides functions to read and write files. Since these functions can modify the contents of a file or create a new file, they are impure functions.
import java.io.FileWriter;
import java.io.IOException;
public class FileIOFunctions {
public static void main(String[] args)throws IOException {
fileIOOperations();
}
public static void fileIOOperations()throws IOException {
FileWriter writer = new FileWriter("example.txt");
writer.write("Hello, world!");
writer.close();
}
}
Network I/O functions
The java.net
package provides functions to communicate with servers over a network. Since these functions can modify the state of a network connection or receive data from a server, they are impure functions.
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class NetworkIOFunctions {
public static void main(String[] args)throws IOException {
networkOperations();
}
public static void networkOperations() throws IOException {
Socket socket = new Socket("www.example.com", 80);
InputStream in = socket.getInputStream();
byte[] data = new byte[1024];
int bytesRead = in.read(data);
socket.close();
}
}
Generally, any function that modifies the program's state or depends on some external state is impure.
Impure functions can make code harder to understand and test, as their behavior can be affected by the external state that is not controlled by the function. While impure functions can be useful in some situations, they should be used sparingly and cautiously.
Gopi Gorantala Newsletter
Join the newsletter to receive the latest updates in your inbox.