Functional programming is a programming paradigm (this is, a way of implementing programs) that focuses on the evaluation of mathematical functions. It is based on lambda calculus, a system born in 1930 to evaluate function definition, application and recursion.
Functional paradigm is declarative, this is, code is made with declarations instead of sentences or statements. In other words, we tell the program how things are, instead of how to solve a problem. On the opposite side we have imperative languages, which are based on commands in the source code, such as assignments, that tell the program how to solve the problem.
Functional programming relies on some fundamental concepts:
Imperative languages can produce side effects in external elements. They also have functions, just like functional programming has, but in this case functions are not mathematical definitions, but a group of sentences that can be called from different parts of the program. Taking into account possible side effects, we might have different results when calling a function many times with the same arguments. Let’s have a look at the following code written in C:
int externalValue = 1;
int aFunction(int param)
{
externalValue++;
return externalValue + param;
}
If we call the function as aFunction(1)
, it will return 3. But if we call it again with the same arguments (aFunction(1)
) it will return 4… and so on. This kind of situation is forbidden in a functional paradigm, although some functional languages let us commit this “crime”.
If we are looking for a functional language, we must distinguish between:
Just to have a first experience with the functional paradigm, let’s see how to solve a typical problem. We are going to use a popular language, such as Java, to illustrate it. We have a list of Person
objects, each one with its own name and age. If we want to get the people that are adults (i.e. their age is greater or equal than 18), we would do it this way with traditional, imperative programming:
List<Person> adultPeople = new ArrayList<>();
for (int i = 0; i < people.size(); i++)
{
if (people.get(i).getAge() >= 18)
adultPeople.add(people.get(i));
}
What we do is explore the original list, and add to a new one every person older than 18. But, if we take advantage of some of the new features provided with Java 8 regarding functional programming, we can solve the same problem like this:
List<Person> adultPeople = people.stream()
.filter(p -> p.getAge() >= 18)
.collect(Collectors.toList());
As we can see, code is more compact, less error-prone and (once we get used to the syntax), more understandable. You can also see how function composition works: the output of stream
method is the input for filter
method, and the output of this method is the input for collect
.
Regarding Java, it has added some functional programming features, specially since version 8, that let us operate with (almost) immutable data, and chain operations through function composition. We are mainly talking about lambda expressions and streams.