Google

May 5, 2014

Transforming your thinking from OOP to FOP with Java 8 examples

One needs to get used to the transformation from imperative programming to functional  programming. You like it or not, you will be using functional programming in Java, and interviewers are going to quiz you on functional programming. Fortunately, Java is not a fully functional programming language, and hence one does not require the full leap to functional programming. Java 8 supports both imperative and functional programming approaches.

Q. What are the differences between OOP and FOP?
A.



In OOP, x = x+ 5 makes sense, but in mathematics or functional programming, you can't say  x = x + 5 because if x were to be 2, you can't say that 2 = 2 + 5. In functional programming you need to say f(x) -> x + 5.

Q. What are the characteristics of functional programming you need to be familiar with?
A.
  1. A focus on what is to be computed rather then how to compute it. 
  2. Function Closure Support
  3. Higher-order functions
  4. Use of recursion as a mechanism for flow control
  5. Referential transparency
  6. No side-effects
Let's look at these one by one:

1. A focus on what is to be computed rather then how to compute it

This was discussed with the following example in the post entitled -- When to use Java 8 functional programming, and where does it shine?


Integer[] numbers = {1,2,3,4,5,6};

//focusses on what to do as opposed to how to do it
//no fluff like for loops, mutations, etc
Arrays.asList(numbers)
       .stream()
       .filter((e) -> (e % 2 != 0))
       .map((e) -> (e * 2))
       .forEach(System.out::println);
    


2. Function closure support

In order to create closures, you need a language where the function type is a 1st class citizen, where a function can be assigned to a variable, and then passed around like any other variables like a string, int or boolean. closure is basically a snapshot of the stack at the point that the lambda function is created. Then when the function is re-executed or  called back the stack is restored to that state before executing the function.

The java.util.function package provides a number of functional interfaces like Consumer<T>, Function&ltT, U>, etc to define closures or you can define your own functional interfaces.

package com.java8.examples;

import java.util.function.Function;

public class ClosureTest {

 public static void main(String[] args) {
  
  //closure 1 that adds 5 to a given number
  Function<Integer, Integer> plus5 = (i) -> (i+5); 
  //closure 2 that times by 2 a given number
  Function<Integer, Integer> times2 = (i) -> (i*2);
  //closure 3 that adds 5 and then multiply the result by 2
  Function<Integer, Integer> plus5AndThenTimes2 = plus5.andThen(times2);  
  //closure 4 that times by 2 and then adds 5
  Function<Integer, Integer> times2AndThenplus5 = times2.andThen(plus5);
    
        //callback or execute closure
  //functions plus5, times2, etc can be passed as arguments
  System.out.println("9+5=" + execute(plus5, 9));
  System.out.println("9*2=" + execute(times2, 9));
  System.out.println("(9+5)*2=" + execute(plus5AndThenTimes2, 9));
  System.out.println("9*2+5=" + execute(times2AndThenplus5, 9));

 }


 //functions can be used as method parameters
 private static Integer execute(Function<Integer, Integer> function, Integer number){
  return function.apply(number); //execute the function
 }
}

Output:

9+5=14
9*2=18
(9+5)*2=28
9*2+5=23

In pre Java 8, you can use anonymous inner classes to define closures. In Java 8, lambda operators like (i) -> (i+5) are used to denote anonymous functions.




Is currying possible in Java 8?

Currying (named after Haskell Curry) is the fact of evaluating function arguments one by one, producing a new function with one argument less on each step.

Java 8 still does not have first class functions, but currying is “practically” possible with verbose type signatures, which is less than ideal. Here is a very simple example:

package com.java8.examples;

import java.util.function.Function;

public class CurryTest {
 
 public static void main(String[] args) {
  Function<Integer,Function<Integer,Integer>> add = (a) -> (b) -> a + b;
  
  Function<Integer,Integer> addOne = add.apply(1);
  Function<Integer,Integer> addFive = add.apply(5);
  Function<Integer,Integer> addTen = add.apply(10);
  
  Integer result1 = addOne.apply(2); // returns 3
  Integer result2 = addFive.apply(2); // returns 7
  Integer result3 = addTen.apply(2); // returns 12
  
  System.out.println("result1 = "  + result1);
  System.out.println("result2 = "  + result2);
  System.out.println("result3 = "  + result3);
 
 }
}

The output is

result1 = 3
result2 = 7
result3 = 12


3. Higher order functions

In mathematics and computer science, a higher-order function (aka functional form) is a function that does at least one of the following:


  1. takes one or more functions as an input -- for example g(f(x)), where f and g are functions, and function g composes the function f. 
  2. outputs a function -- for example, in the code above plus5 and plus2 outputs a function


        Function<Integer, Integer> plus5 = (i) -> (i+5); 
 //closure 2 that times by 2 a given number
 Function<Integer, Integer> times2 = (i) -> (i*2);


also

        Function<integer integer> plus5AndThenTimes2 = plus5.andThen(times2);  


outputs another function, where the plus5 function takes plus2 function as an input.


4. Use of recursion as a mechanism for flow control

Java is a stack based language that supports reentrant (a method can call itself) methods. This means recursion is possible in Java. Using recursion you don't need a mutable state, hence the problems can be solved in a much simpler fashion compared to using an iterative approach with a loop.

package com.java8.examples;

import java.util.function.Function;

public class RecursionTest {
 
   static Function<Integer, Integer> factorial = 
       x -> { 
           return  ( x == 0) ? 1 : x  * factorial.apply(x-1);
                  };
 
  public static void main(String[] args) {
       
       int result = factorial.apply(3);
       System.out.println("factorial of 3 = " + result);
  }
}


Recursion is used in the Lambda expression to calculate the factorial.


5. Referential Transparency

Referential transparency is a term commonly used in functional programming, which means that given a function and an input value, you will always receive the same output. There is no external state used in the function. In other words, a referentially transparent function is one which only depends on its input. The plus5 and times2 functions shown above are referentially transparent.

A function that reads from a text file and prints the output is not referentially transparent. The external text file could change at any time so the function would be referentially opaque


6. No side-effects




One way to do programming without side effects is to use only immutable classes. In real world, you try to minimize the mutations, but can't eliminate them. Lambdas can refer to local variables that are not declared final but are never modified. This is known as "effectively final". 

When you are using methods like reduce on a stream, you need to be aware of the side effects due to parallelism. For example, the following code will have no side effects when run in serial mode.


public static void main(String[] args) {  
 Integer[] numbers = {10, 6};
 Integer result = Arrays.asList(numbers).stream()
                      .reduce(20, (a,b) -> (a - b));
          
 System.out.println(result);
}


Outputs:

4

It starts with 20, and then subtracts 10 and 6. 20 - 10 - 6 = 4; But if you run it again in parallel mode as shown below

public static void main(String[] args) {  
 Integer[] numbers = {10, 6};
 Integer result = Arrays.asList(numbers).parallelStream()
                         .reduce(20, (a,b) -> (a - b));
          
 System.out.println(result);
} 


The output will be:

-4

What happened here? 10 - (20 - 6) =  -4;

This means the reduce method on a stream produce side effects when run in parallel for non-associative operations.

Q. What is an associative property?
A. Associative operations are operations where the order in which the operations were performed does not matter. For example.

Associative:

3 + (2 + 1) = (3+ 2) + 1
3 * (2 * 1) = (3 * 2) * 1

Non-associative:

3 - (2 - 1) != (3 - 2) - 1
(4/2)/2 != 4/(2/2)


Pure functions are functions with no side effects. In computers, idempotence means that applying an operation once or applying it multiple times has the same effect. For example

Idempotent operations

  • Multiplying a number by zero. No matter how many times you do it, the result is still zero.
  • Setting a boolean flag. No matter how many times you do it, the flag stays set.
  • Deleting a row from a database with a given ID. If you try it again, the row is still gone.
  • Removing an element from a collection.
In real life where you have concurrent operations going on, you may find that operations you thought were idempotent cease to be so. For example, another thread could unset the value of the boolean flag.

Labels: ,

Apr 29, 2014

When to use Java 8 functional programming, and where does it shine?

Q. What is the difference between imperative and declarative programming paradigms?
A. Imperative (or procedural) programming: is about defining the computation how to do something in terms of statements and state changes, and as a result what you want to happen will happen.

Declarative programming: is about declaratively telling what you would like to happen, and let the library or functions figure out how to do it. SQL, XSLT and regular expressions are declarative languages.

Q. Does functional programming use imperative or declarative approach?
A. Functional programming is a form of declarative programming, where functions are composed of other functions -- g(f(x)) where g and f are functions. An output of one function becomes the input for the composing function. A typical example of functional programming, which you may have used  is transforming an XML document using XSLT to other forms. The composable and isolated  XSL style sheets are used for transformation.


In imperative programming it makes sense to say x = x + 1, but in maths or functional programming it is not right to say x = x + 1, if x is 2 is it right to say 2 = 2 + 1 or 2 = 3 ?In functional programming f(x) -> x + 1, where  f is a function that takes an argument of x.

Q. What does functional programming shine in Java 8?
A.

Scenario 1: Here is an imperative program to extract odd numbers from a given list of numbers and then double each odd number and print each of them.



Imperative program with looping, statements, etc.


package com.java8.examples;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class NumberTest {

 public static void main(String[] args) {
  Integer[] numbers = {1,2,3,4,5,6};
 
   long start = System.nanoTime();
 
   //odd numbers
   List<Integer> oddNumbers = new ArrayList<>();
   for (Integer e : numbers) {
   if(e %2 != 0) {
    oddNumbers.add(e);
   }
   }
   
   //doubled odd numbers
   List<Integer> doubledOddNumbers = new ArrayList<>();
   for (Integer e : oddNumbers) {
         doubledOddNumbers.add(e * 2); 
   }
   
   
   //print each
   for (Integer e : doubledOddNumbers) {
     System.out.println(e);
   }
   
     
   System.out.println("Completed in " + (System.nanoTime() - start) + " nano second");
   
 }
}


Output:

2
6
10
Completed in 371880 nano second

Functional programming using the Java 8 lambda expressions.

Functional programming with good set of libraries can cut down lot of fluff and focus on just transformations. In other words, just tell what you would like to do.

package com.java8.examples;

import java.util.Arrays;

public class NumberTest {

 public static void main(String[] args) {
  Integer[] numbers = {1,2,3,4,5,6};
 
   long start = System.currentTimeMillis();
 
   //static methods are used for clarity
   Arrays.asList(numbers)
       .stream()
       .filter((e) -> (e % 2 != 0))
       .map((e) -> (e * 2))
       .forEach(System.out::println);
   
   
   System.out.println("Completed in " + (System.currentTimeMillis() - start) + " ms");
   
 }
  
}


Output:

2
6
10
Completed in 46 ms

Shining moment 1: The above code has increased readability and maintainability because each function is designed to accomplish a specific task for given arguments. Lesser moving parts than the imperative code. The above code can be further improved by extracting the cryptic lambda expressions like  (e) -> (e * 2) to static methods with meaningful names for re-usability and readability as shown below.

package com.java8.examples;

import java.util.concurrent.TimeUnit;
import java.util.Arrays;

class NumberTest {

 public static void main(String[] args) {
   Integer[] numbers = {1,2,3,4,5,6};
 
   long start = System.currentTimeMillis();
 
   //static methods are used for clarity
   Arrays.asList(numbers)
       .stream()
       .filter(NumberTest::isOddNumber)
       .map(NumberTest::doubleIt)
       .forEach(System.out::println);
   
   System.out.println("Completed in " + (System.currentTimeMillis() - start) + " ms");
   
 }

 private static boolean isOddNumber(int number){
  //simulate time to perform an operation
  delay();
  System.out.println("isOdd " + number);
  return number % 2 != 0;
 }
 
 private static int doubleIt(int number){
  //simulate time to perform an operation
  delay();
  System.out.println("doubleIt: " + number);
     return number * 2;
 }
 
 private static void delay() {
  //simulate time to perform an operation
  try {
   TimeUnit.SECONDS.sleep(1);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}

Output:

isOdd 1
doubleIt: 1
2
isOdd 2
isOdd 3
doubleIt: 3
6
isOdd 4
isOdd 5
isOdd : 5
10
isOdd 6
Completed in 9454 ms

Shining moment 2: A time delay was added on purpose to demonstrate that a functional program can be easily made to run in parallel using the "fork and join" feature added in Java 7. To improve performance of the above code that took 9.5 seconds to run, all you have to do is use .parallelStream( )   instead of .stream( ).

   Arrays.asList(numbers)
       .parallelStream()      //use fork and join 
       .filter(NumberTest::isOddNumber)
       .map(NumberTest::doubleIt)
       .forEach(System.out::println);



The new output due to parallel processing will be:

isOdd 2
isOdd 4
isOdd 1
isOdd 6
isOdd 3
doubleIt: 1
isOdd 5
2
doubleIt: 3
6
doubleIt: 5
10
Completed in 3151 ms

Finished running in 3 seconds  with the performance gain. When parallelising tasks, you need to be

  • Ensuring that the computation is correct
  • Ensuring that parallelism actually brings performance gains
  • Using algorithms that are parallelisable


Shining moment 3: code is easier to refactor as shown below. If you want to switch the logic to double it first and then extract the odd numbers, all you have to do is swap the .filter call with .map call.

 Arrays.asList(numbers)
       .parallelStream()
       .map(NumberTest::doubleIt)
       .filter(NumberTest::isOddNumber)
       .forEach(System.out::println);

The output will have no odd numbers at all.

doubleIt: 4
doubleIt: 2
doubleIt: 1
doubleIt: 6
isOdd 4
isOdd 12
isOdd 2
isOdd 8
doubleIt: 5
doubleIt: 3
isOdd 10
isOdd 6
Completed in 4145 ms


Shining moment 4: Easier testing and debugging. Because pure functions can more easily be tested in isolation, you can write test code that calls the pure function with typical values, valid edge cases, and invalid edge cases. In the above example, I was using the Java 8 library that was well tested with confidence. I will only have to provide unit tests for the static functions boolean isOddNumber(int number)  and  int doubleIt(int number) that provide the logic.


Having said this, OO programming and functional programming can co-exist. In the next post I will discuss how to get accustomed to Function Oriented Programming (FOP) from Object Oriented Programming (OOP).

Labels: ,