Java Functional Programming : 1. Introduction
This is the multi part series on Java Functional Programming
1. Introduction
As per Wikipedia – “In
computer science, functional programming is a programming paradigm—a style of
building the structure and elements of computer programs—that treats
computation as the evaluation of mathematical functions and avoids
changing-state and mutable data.” Lambda Calculus was first formulated
by Alonzo
Church in 1930. Functional Programming finds its roots in the Lambda
Calculus.
Let us see a quick comparison
between the two programming paradigms – Imperative and Functional. This is
independent of Java programming language and will help us build the concepts later.
Difference Between Imperative &
Functional Languages
|
||
Imperative Language
|
Functional Language
|
|
Point 1
|
The assignments can change over the course of program execution.
|
A reference is associated with only one value, they do not change,
are immutable
|
Point 2
|
They are dependent on the order in which the elements are
evaluated.
|
There is no necessary
execution order. Of course, the programs must be executed in an order, but
they do not alter the outcome. This is one of the strengths of functional
programming.
|
Point 3
|
Iterations are usually done by value reassignment.
|
Iterations are achieved by making recursive calls to the
function.
|
Point 4
|
Allows only primitive data and object reference
|
Functional languages allow functions to be treated as values (or
references) along with primitives and objects
|
Let us review the difference with
an example. Let us say you iteratively take first 4 integers (starting from 1)
and double those values and print them. This is how you would do it in Java.
public void doubleInt(){
for (int i=1; i<=4; i++){
System.out.print(2*i);
}
}
|
And these are the numbers that
get printed.
(2, 4, 6, 8)
What you have done is : taken a
number -> doubled it -> printed it, and then went back to the loop,
assigning the variable with the next number and repeating the process, till the
end condition is achieved. The code could also be written as (though it is not
recommended) below to show the steps.
public void doubleIntBlown(){
int i=1; // initialized a value for a variable
for (;;){
if (!(i<=4)){ // checking for a filter to exit
break;
}
int j = 2*i; // doubled the number
System.out.print(j); // and printed it
i = i+1; // re-assigned a new value
}
}
|
If this were to be done using the functional programming paradigm, this is how it would be written (not using the Lambda Expressions for now), basically we can write it simply as:
(doubleOf ( take 4 (integers)))
The statement contains one
constant 4, and statements. You really don’t need any variable (like “i”, in
the example above) to resolve this. Actually most functional programming
languages would have a similar constructs. These are statements are evaluated
step by step, one function at a time. So the first step is:
(doubleOf ( take 4 (1, 2, 3,
………..)))
If you think it is copy and
paste, this is what precisely happens in most of the programs. The next step
becomes:
(doubleOf (1, 2, 3, 4))
What we have done is filtered the
numbers to first 4, from an integer set. Now we will double the numbers, and
get the final output as:
(2, 4, 6, 8)
Notice that there are no
variables needed, and so don’t have to worry about the assignments. We instead
passed functions like – integers, take and doubleOf in the program (Point 4,
above). If you could look into the memory structure of the program you’d find
that each element which has been initialized has retained its values till the
end. They have not changed. This is indeed a necessary attribute of functional
programming and “Referential Transparency”.
It means that the function must always return the same values if its
input is the same. This also implies that the function should be dependent on
only the values which are passed within in, and not on anything else.
Functional programming mandates that the objects created in the process are immutable
and do
not change over the course of its execution (Point 1, above). Since the
method call do not depend on assigned values, they are also independent of the
order in which they are executed (Point 2, above). That is we could have first
doubled the values and then taken the first 4 values from the doubled numbers,
and would have got the same result. That is:
(doubleOf ( take 4 (integers)))
is equivalent to the statement
below and the order does not matter.
(take 4 (doubleOf (integers)))
We would keep revisiting the
functional programming as we study the “Lambda
Expressions”, and as we dive deeper in Java’s functional programming
constructs. One reason why functional programming is important is the way
processing happens on the computers. Computers have reached a threshold on the
processing speeds, and we are adding to more processing power by adding more
cores. Who knows how many cores future computers would have. Java is perhaps
bracing up for that. Keeping objects immutable is the only way where we could
leverage the power of “parallel”
processing. When we are sure that all others threads which are accessing a
memory area, are not allowed to modify it, we can work parallely with those values. In case those values are modified, then we have to use “synchronization” to make sure only one
thread has access to it one point of time. But we already know, making
something “synchronized” comes with
the cost of efficiency of execution.
Let us see a java code on how the
same can be achieved using the lambda expressions in Java. Do not
worry if you don’t get it now. We shall come back to it many times, till we are
clear on everything mentioned in the code. This is just for the sake of
completion and we shall detail this after we understand what a “Lambda
Expression” means:
// created an expression to filter the numbers
// choose top 4 integers
Predicate<Integer> filter = (i) -> i <= 4;
// created an expression to double the number
UnaryOperator<Integer> dInt = (i) ->
2*i;
// get a list of integers say 1-10 (it was infinite
in the above example),
List<Integer> iList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// and then try to filter it using streams
Stream<Integer> iStream = iList.stream();
// the actual operation, where you first filtered and
then applied the operator
iStream.filter(filter).map(dInt).forEach(System.out::print);
|
Line 1 of code: Define an expression to get the numbers less than 4, this is for filtering
Line 2 of
code: Define an expression to double the number
Line 3 of
code: Get a list of integers starting 1 to 10 (or more if you please)
Line 4 of
code: Get a stream of integers defined above
Line 4 of
code: Filter the numbers double it and print the values
And this is
printed when the above code snippet is executed - 2468
You can write all this in just
one line, without even declaring variables.
List<Integer> iList2 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
iList2.stream().filter(i -> i <=4).map(i -> 2*i).forEach(System.out::print);
|
Next (Lambda & Functional Interfaces) >>
Comments
Post a Comment