Functional Programming 1675501627
Functional Programming 1675501627
1. Lambda Calculus
Simply put, the declarative approach's focus is to define what the program
has to achieve rather than how it should achieve it. Functional
programming is a subset of the declarative programming languages.
These categories have further subcategories, and the taxonomy gets quite
complex, but we won't get into that for this tutorial.
Other languages allow both functional and procedural programs and are
considered impure functional languages. Many languages fall into this
category, including Scala, Kotlin and Java.
Please note that many features we'll be using haven't always been part of
Java, and it's advisable to be on Java 8 or later to exercise functional
programming effectively.
This means that functions are allowed to support all operations typically
available to other entities. These include assigning functions to variables,
passing them as arguments to other functions and returning them as values
from other functions.
Fortunately, Java 8 brought many new features to ease the process, such as
lambda expressions, method references and predefined functional interfaces.
Let's see how a lambda expression can help us with the same task:
However, please note that while this may give us the impression of using
functions as first-class citizens in Java, that's not the case.
Behind the syntactic sugar of lambda expressions, Java still wraps these
into functional interfaces. So, Java treats a lambda expression as an Object,
which is the true first-class citizen in Java.
This can sound quite contrary to all the best practices in Java.
Side effects can be anything apart from the intended behavior of the
method. For instance, side effects can be as simple as updating a local or
global state or saving to a database before returning a value. (Purists also
treat logging as a side effect.)
So, let's look at how we deal with legitimate side effects. For instance, we
may need to save the result in a database for genuine reasons. There are
techniques in functional programming to handle side effects while retaining
pure functions.
3.3. Immutability
Please note that Java itself provides several built-in immutable types, for
instance, String. This is primarily for security reasons because we heavily
use String in class loading and as keys in hash-based data structures. There
are also several other built-in immutable types such as primitive wrappers
and math types.
But what about the data structures we create in Java? Of course, they are
not immutable by default, and we have to make a few changes to achieve
immutability.
The use of the final keyword is one of them, but it doesn't stop there:
public class ImmutableData {
private final String someData;
private final AnotherImmutableData anotherImmutableData;
public ImmutableData(final String someData, final AnotherImmutableData
anotherImmutableData) {
this.someData = someData;
this.anotherImmutableData = anotherImmutableData;
}
public String getSomeData() {
return someData;
}
public AnotherImmutableData getAnotherImmutableData() {
return anotherImmutableData;
}
}
public class AnotherImmutableData {
private final Integer someOtherData;
public AnotherImmutableData(final Integer someData) {
this.someOtherData = someData;
}
public Integer getSomeOtherData() {
return someOtherData;
}
}
However, several external libraries can make working with immutable data
in Java easier. For instance, Immutables and Project Lombok provide
ready-to-use frameworks for defining immutable data structures in Java.
This is a typical POJO class in Java, but we're interested in finding if this
provides referential transparency.
The three calls to logger are semantically equivalent but not referentially
transparent.
The first call is not referentially transparent since it produces a side effect.
If we replace this call with its value as in the third call, we'll miss the logs.