Advertisement

Sri Lanka's First and Only Platform for Luxury Houses and Apartment for Sale, Rent

Wednesday, November 12, 2014

Java 8 : Not a Paradigm Shift. Just Revisiting the Old Roots 2

This post is a continuation of previous post.

Recently I was privileged enough to see two great talks by two Industry Experts. Namely Jessica Kerr and Dr. Venkat Subramaniam and I have the links to those talks at references section and I strongly advise to watch those. First I watched Jessica Kerr's talk on Functional Principles for Object-Oriented Developers and secondly I watched Dr. Subramaniam's talk on Java 8 Language Capabilities. What's in It for You?. I am really glad that I watched those videos in that order because it made me understand the problems prior to Java 8 and how Java 8 has solved those problems elegantly.

Main takeouts from Jessica's talk are the followings;
  1. Function/Methods should not modify shared mutable states.
  2. Function/Methods should not modify input parameters.
  3. Function/Methods should not do harm to the world.
  4. Function/Methods should not change the execution flow (Exceptions must be returned as data not thrown).
She clearly goes through the benefits of being functional and how it can be achieved in different languages including Java prior to Java 8 using Google Guava library. She further explains that our code must move away from being Imperative to being more Functional/Declarative. In the sense we must say what we are need to do but not how to do it. One great example she points out is SQL. SQL language is highly declarative where we don't care how Oracle, MySQL, MS SQL RDBMS's store data underneath. We just say I want all the data of customers by just issuing "SELECT * FROM Customers". This is a really strong concept because this gives the actual Oracle, MySQL, MS SQL implementations to change transparently without ever breaking the client code. Those RDBMS's can retrieve data sequentially, parallel or in any other form. More on this through the words of Jessica herself;

"The power of SQL's declarative style isn't about swapping out Oracle for MySQL; any standard can do that. It's more about, for a given query, the database is free to swap out its strategy based on the shape of the data.
Say you want to count customers in the UK with open orders. (approximate syntax, crude table design)

SELECT COUNT(DISTINCT customer_id) FROM customers JOIN orders ON customer_id
WHERE orders.status = 'OPEN'
AND customer.country = 'UK'

If both tables are small, the database might start with the join and then filter.
If the order table is huge but very few of them are OPEN, the database might start by finding open orders, then connect them to customers, then filter again.
If the customers table is partitioned, the database might split up the table and run in parallel on each partition.

This is the power of a declarative style, of telling the database WHAT to do, not HOW to go about it. All within one underlying database implementation!"

Dr. Subramaniam's talk further verifies the claims of Jessica and explains in detail how Java 8 allows Developers to make the transition from being Imperative to being more Functional/Declarative by introducing Lambda Expressions and Streams. Lambda Expressions makes functions act also as first class citizens allowing Functional/Declarative style coding. One code excerpt I took from Dr. Subramaniam's talk is the following;

import java.util.List;
import java.util.Arrays;
import java.util.function.Consumer;

public class Sample {

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        //Imperative
        //External Iteration

        for(int i = 0;i<numbers.size();i++) {
            System.out.println(numbers.get(i));
        }

        for(int e:numbers) {
            System.out.println(e);
        }

        //Internal Iteration

        numbers.forEach(new Consumer<Integer>() {
            public void accept(Integer no) {
                System.out.println(no);
            }
        });

        numbers.forEach((Integer no) -> System.out.println(no));

        numbers.forEach((no) -> System.out.println(no));

        numbers.forEach(no -> System.out.println(no));

        numbers.forEach(System.out::println);

        //Functional/Declarative
    }
}

The above code can be described as the blue print of Imperative to Functional/Declarative evolution in Java. The code begins being highly Imperative where we need to tell how to loop a Collection of numbers then it gradually evolves to become a highly Functional/Declarative where Iteration and Logic is handed over to the Collection itself. All the codes print out the same results.

Furthermore he shows how mutations takes place with Imperative code and how we can eliminate mutations by making use of Functional/Declarative style of coding in Java 8. Following code describes;

import java.util.List;
import java.util.Arrays;
import java.util.function.*;

public class Sample1 {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // Imperative
        int total = 0;
        for(int e:numbers) {
            total += e;
        }    
        System.out.println(total);

        // Functional/Declarative
        System.out.println(numbers.stream()
                                  .reduce(0, (c, e) -> { return c + e; }));

        // Imperative
        total = 0;
        for(int e:numbers) {
            if(e % 2 == 0) {
                total += e;
            }
        }     
        System.out.println(total);

        // Functional/Declarative
                System.out.println(numbers.stream()
                                          .filter(e -> e % 2 == 0)
                                          .reduce(0, (c, e) -> { return c + e; }));
        
        // Imperative
        total = 0;
        for(int e:numbers) {
              total += (e * 2);
        }     
        System.out.println(total);

        // Functional/Declarative
                System.out.println(numbers.stream()
                                          .map(e -> e * 2)
                                          .reduce(0, (c, e) -> { return c + e; }));

        // Imperative
        int no = 0;
        for(int e:numbers) {
             if(e > 3 && e % 2 == 0) {
                 no = e;
                 break;
             }  
        }     
        System.out.println(no);

        // Functional/Declarative
        System.out.println(numbers.stream()
                                  .filter(e -> e > 3)
                                  .filter(e -> e % 2 == 0)
                                  .findFirst()
                                  .orElse(0));

        // Imperative
        System.out.println(total(numbers, e -> true));
        System.out.println(total(numbers, e -> e % 2 == 0));
        System.out.println(total(numbers, e -> e % 2 == 1));

        // Functional/Declarative
        System.out.println(total2(numbers, e -> true));
        System.out.println(total2(numbers, e -> e % 2 == 0));
        System.out.println(total2(numbers, e -> e % 2 == 1));

    }

    public static int total(List<Integer> numbers, Predicate<Integer> condition) {
        int total = 0;
        for(int e:numbers) {
            if(condition.test(e)) {
                total += e;
            }
        }
        return total;
    }

    public static int total2(List<Integer> numbers, Predicate<Integer> condition) {
        return numbers.stream()
                      .filter(condition)
                      .reduce(0, (c, e) -> c + e);
    }
    
}

The fact of the matter is Imperative code with mutations can cause headaches and hair loss if they need parallelism. Where as the Functional/Declarative style of coding in the above code can be parallelized by just changing "numbers.stream()" to "numbers.parallelStream()". That's it.

This is because in Functional/Declarative we are saying what needs to be done not how to do it. So how to do it can be decided by the JVM just as how RDBMS decide how to retrieve data for a particular SQL query. Furthermore Functional/Declarative style is combined with Lazy (Efficient) execution where there are Intermediate (map, filter) and Terminal (reduces, findFirst) functions. The Stream will not be evaluated until we call a Terminal function making our code efficient.

Both of the speaker agree on one thing. Familiar code is not necessarily Readable code. Just because we have written for loops to iterate a zillion times doesn't mean it is not complex. Don't be fooled by thinking that because Java 8 Functional/Declarative style of coding is Complex because it looks Unfamiliar. May be it is the best and efficient way to do what you really want to do.

I would like to end this by a quote by Dr. Subramaniam, he states "The biggest change in Java 8 are in the minds of the programmers".

References
  1. Functional Principles for Object-Oriented Developers - By Jessica Kerr
  2. Java 8 Language Capabilities. What's in It for You? - By Dr. Venkat Subramaniam

Java 8 : Not a Paradigm Shift. Just Revisiting the Old Roots 1

When I started my Bachelor's in Software Engineering back in 2007 during very early stages I was attending a Module for Programming Principles and there the lecturer said that our programs must never use global variables (Module was using C++) and each problem should be broken into a Function/Method essentially the practice Functional Decomposition. But during this time I was working as a Java Developer for a Company and my inner Java Developer opposed to this.


As Java being an Object Oriented Programming language, we are advised to think in terms of Objects rather than Function/Method. Practically functions/methods are second class citizens in Java. There is no way a Function/Method can exist without it being attached to a Class or being part of an Object. So I thought Functional Decomposition is old school and was only applicable for languages that is highly function oriented like C/C++.

But couple of years back I started reading the book Java Concurrency in Practice book and it stated that thread safety is all about protecting shared mutable state. If a Class in Java has no instance or static variables that Class becomes thread safe. One theory I learned from that book is that,

"If there are shared mutable states in a Class and if more than one thread reads those states and also if at least one thread modifies those states then access (read/modification) to those states must be synchronized"

So it all came back to me, What I learnt in 2007 as part of my Bachelor's is actually part of Java also. Global variables can be considered as shared mutable states. It is better if a Class has lesser no of shared mutable states so that in can become more concurrent friendly. Because synchronization is costly, be it Monitor lock, Copy-On-Write, Compare-And-Swap, etc. all must be used to a minimum level with shared mutable variables or the program performance can degrade.

Best approach is Immutability in classes. Immutable classes are inherently thread safe and performance degrading is not a concern. For example Java String class immutable being that after instantiate and initialization its state can not be modified. Invoking almost every Method on a String object will produce a new String object.

Furthermore the theory I learned in 2007 is so much alive today. Functional Decomposition is actually the ideal solution for today's problems. Problems broken down into Functions/Methods are easier to code/test/debug/maintain.

Next we'll see how Java 8 Helps to achieve Immutability and Functional Decomposition.

References

  1. Java Concurrency in Practice - http://jcip.net/