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: ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home