Functional Programming In Jdk8
-
Upload
bansilal-haudakari -
Category
Technology
-
view
36 -
download
1
Transcript of Functional Programming In Jdk8
Functional Programming – A Hands On
Introduction To JDK 8
Bansilal Haudakari
Technology Evangelist
About Me Seasoned Programmer writing Code since Jdk1.0
Experienced developing software products for FX Trading, Content Management, IP Address Management, EAI at Citi, Oracle, Boeing and Intuit
Core Technologist at heart and believes in innovation culture
Passionate about building next generation products and platforms
Agenda Evolution of JAVA Functions Are First Class Citizens Your First Lambda with Uncle SAM Paradigm Shift In Programming : from
Imperative to Functional Lambda Under The Hood : Welcome Indy Streams
- Efficiency Of Streams – Lazy EvaluationJava 8 Best PracticesReactive Programming
Java Evolution
Evolution Of Java
Evolution Of Java
Evolution Of Java Jdk1 : Statically Type : Type of all variables is known or inferred at Compile Time Threading
Jdk5 Generics : provide compile-time type safety to catch invalid types Annotations
Jdk 8 Type Inference : compiler’s ability to look at each method invocation &
corresponding declaration to determine type argument /args that makes invocation applicable. Finally inference algo i.e. indy determines the most specific type
Indy Streams
What Is FP (Functional Programming)?• FP deals with code in the same way as data. This means
that a function should be a first-class value and able to be assigned to variables, passed to functions and so forth.
• Programs are written in terms of functions that always return the same output for a given input (regardless of any other state present in the running program) and that do not cause any other effects or change any program state. Functions that obey this are sometimes called "pure" functions, and they behave in the same way that mathematical functions do.
• pure functions does not depend on external state & can easily be combined together - strong functional heritage
Examples of FPClojure function, e.g. log-processing function shown below, is
a first-class citizen, and doesn’t need to be bundled up in a class to exist.
(defn build-map-http-entries [log-file] (group-by :uri (scan-log-for-http-entries log-file)))
Scala permits functions as values, such as:val sqFn = (x: Int) => x * x
whilst retaining class and object syntax that is very close to that of Java.
Functions Are First Class Citizens In OOP Pass Objects to methods Create Objects within methods Return Objects from within methods
In FP: Pass Functions to functions Create Functions within functions Return Functions from functions
How Functional Is Java ?• Classes are the heart of the Java platform - a class is the basic
unit of functionality that the Java platform will load, link - and all code that is intended for execution must live inside a class.
• Interfaces can't be instantiated directly, and instead a class must be defined that implements the API defined by the interface.
• Arrays hold either primitive types, or instances of classes, or other arrays.
• The primitive types are all defined by the platform.
How Functional Is Java ?• Strongly type PL : Types are either reference types or primitive
types. Reference types are classes, interfaces or arrays. • Every type must have a name that it can be referred by. Even
the so-called "anonymous inner classes" still have a type by which the programmer must refer to them - the type of the interface that they implement:
Runnable r = new Runnable() { public void run() { System.out.println("Hello World!"); } };
• Another way of saying this is that every value in Java is either a primitive or an instance of some class.
Java 5 Type System• 3 major features to the type system - enums, annotations and generic types.• Enumerated types (enums) are similar to classes in some respects, but they have
the property that only a specified number of instances may exist, and each instance is specified in the class description and distinct. Intended primarily as a "typesafe constant" rather than the then-common practice of using small integers for constants, the enum construction also permits additional patterns that are sometimes extremely useful.
• Annotations are related to interfaces - the keyword to declare one is @interface - with the initial @ indicating that this is an annotation type. As the name suggests, they're used to annotate elements of Java code with additional information that doesn't affect behavior. Previously, Java had made use of "marker interfaces" to provide a limited form of this metadata, but annotations are considerably more flexible.
• Java's generics provide parameterized types - the idea that one type can act as a "container" for objects of another type, without regard for the specifics of exactly which type is being contained. The type that fits into the container is often called the type parameter.
Java 8 Type System• Is it still a Named type system? Here is an example from Java 8:
() -> { System.out.println("Hello World!"); }
This is a method, without a name, that takes no parameters and returns void. It's still perfectly statically typed, but is now anonymous.
Have we escaped the Kingdom of the Nouns? Is this actually a new form of type for Java?Answer : No.
The JVM, on which Java and other languages run, is very strictly tied to the concept of classes. Class loading is central to the Java platform's security and verification modes.
So, it would be very, very difficult to conceive of a type that was not, in some way, represented through a class
Lambda ExpressionsThanks to Uncle SAM
Sum(x,y) -> x +y(x,y) -> x +y
Your First Lambda Expression
new Thread(new Runnable() {
@Overridepublic void run() {
System.out.println("It runs !"); }}).start();
new Thread(() -> { System.out.println("It runs !"); }).start();
Lambda Expression A Lambda expression is an anonymous functions – Which are like methods but not associated with a class
() -> { System.out.println("It runs !"); } Syntax: ( parameters ) -> { lambda-body }
Where “->” is a Lambda Operator
Rules: Lambdas with a
– single parameter do not need brackets – no parameters must have empty brackets– Single line Lambdas do not need braces or explicit return statement– Body of the Lambda may throw exceptions
Simple Rule : Brackets and braces are optional for certain situations
Examples of Lambda Expression () -> System.out.println("Hello Lambda Expression")
x -> x + 10
(int x, int y) -> { return x + y; }
(String x, String y) -> x.length() – y.length()
x -> { if((x % 2)==0)
return "even" else
return "odd" }
Where to Use Lambda Expression A Lambda expression can be used wherever the type is a Functional Interface
– This is a single abstract method type SAM
– The Lambda expression provides the implementation of the single abstract method
– The OfficeSpace pattern where Mr Ted says i take the business reqmts and give it to programmers for execution
Paradigm Shift : Imperative To FunctionalList<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);for(int i = 0; i < numbers.size(); i++) { System.out.println(i); } JDK5: for(int e : numbers) { System.out.println(e); }
How does this code looks to you?
• Simple !!!
• Its Familiar but Not Simple because it has code smells !!!!
Code Smells• Imperative – saying How • Mutability – moving parts: starts with an int “I” value and then increment “I” and check
boundary limits so the focus is more on moving parts that means more errors & bugs in the code when the volume of mutation is more.
hard to make concurrent as well;
so lets refactor it into declarative style where the focus is not on How but on What and on Immutability as they are really powerful and also its functional style
Functional ProgrammingV1(Version 1) : similar to what we have done before. Consumer is new Functional Interface which has an accept method which takes a value
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
numbers.forEach( (Integer number) -> System.out.println(number); });
• Note methods like forEach work on Collections; takes specific Functional Interface as an argument e.g. Consumer
• Functional Interface defines the contract for number of parameters. For example if you send more than one parameter you get compilation error.
Functional ProgrammingV2 (Version 2) :
numbers.forEach((Integer number) -> System.out.println(number)); Type Inference V3(Version 3): Obvious : Life Lesson Obvious : You knows It, Java Knows It, So Why bother declare the Type. Java is finally Intelligent !! Note: Type Inference is ONLY for lambda expressions
numbers.forEach(number -> System.out.println(number));
Method Reference V4 (Version 4): numbers.forEach(System.out::println);
Lambda A Peek Under The HoodAre Lambda’s converted to Anonymous Inner Classes At Compile Time??Illusion Syntax Sugar? Compiler replace Lambda with Anonymous Inner class. Lets examine Step 1 : Write a Sample.java with anonymous inner class e.g. new Consumer and
compile it . You will see two .class files generated
Step 2 : Replace the anonymous inner class with lambda expression and compile it . You will see single .class files generated.
Repeat Step 1 with multiple copy/paste snippet of anonymous inner class. You will see multiple .class files generated
Repeat Step 2 with multiple copy/paste snippet of lambda expression. You will see single .class files generated.
So what’s really happening here?Its something Amazingly Cool !!! Lets see how
Welcome Indy$ javap -c HelloLambda //disassembles a .class file into readable formatpublic class HelloLambda { public HelloLambda(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return
public static void main(java.lang.String[]); Code: 0: invokedynamic #2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 5: astore_1 6: aload_1 7: invokeinterface #3, 1 // InterfaceMethod java/lang/Runnable.run:()V 12: return}
invokedynamic
Who is Indy ? Indy is invokedynamic i.e. invoke + dynamic Introduced in Jdk7 as a feature implemented for dynamically type
languages e.g. Scala, Jruby A Bytecode JIT level Optimization. Rewrote in Jdk8 to support use case of lambda expressions : no
longer anonymous inner classes Lambda Expression does not get replaced by Anonymous Inner
Classes i.e. they do not have the overhead of creating/deleting /GC Anonymous Objects. Reduced Runtime Memory Foot-Print !!
Life Lesson : Act of karma : if you do good things to others it will comes back to you
How Indy works?• Attach & de-attach to a function you want to invoke dynamically and
re-attach at runtime : call site function• Function Pointers are available at the JVM level for us to program
How is it implemented? When you create lambda expression, the call to Lambda Expression
will simply become invoke dynamic and the lambda expression itself will be one of the 3 based on the context:
A static method A instance method A routing for invoke dynamic method to another method in existing
class
StreamsThe Real Power House of Java 8
Streamsstream():- Operates on collection of values- A fancy iterator which gives many number of functions unlike
older iterator hasNext();- Interface to process collection of data- Express aggregation and reduction- Facilitate operation pipelining on bulk data
Streams include:Lambda operators (map,filter, sort, projections, etc) Terminal operators (collect, reduce, forEach, min, max, etc)Streams are lazily executed (at terminal operator)
Map the Streams - map() : Transforms Values - A map applies a function to input element and gives output element similar to
mathematical function which transforms input to output
- Number of elements in the Input = Number of elements in the Output e.g. I need the names of all the people in the room i.e. names = number of people
- But no guarantee on the type of output w.r.t input for e.g. I need the phone numbers of all the people in the room so its mapping of Name to Phone#
- Example : map( e -> e*2) of same type ; map( e -> e*2.0) – transformation type
- Syntax : Parameter of Stream<T> coming in as Input ; map applies a Function<T, R> to a input stream of type <T> and returns a stream of type R Stream<R>
DemoImperative:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); int totalOfValuesDoubled = 0; for(int number : numbers) { totalOfValuesDoubled += number * 2; } System.out.println(totalOfValuesDoubled);
Functional:
System.out.println( numbers.stream() .mapToInt(number -> number * 2) .sum()); // Also this works : reduce(0, (c , e) -> c+e));
Reduce On Streamsreduce() : Transforms the collection into a single value or non-stream itself- A reduce applies a function which takes two input and give one output i.e. Binary
Function. E.g. reduce(0, (c , e) -> c+e)- Reduce starts from 0 , takes carry over and element as another input and then does
the sum of carry over and element. Note carry over is from map.- Reduce on Stream<T> takes two parameters:- First parameter is of type T- Second parameter is of type BiFunction<R, T, R> to produce a result of type R- Where T is the element and R is both the input and output type because input at one
stage which in turn becomes output- Specialized Reduce Functions:• Sum()• Collect()
toList() toMap() toSet()
Filter The Streamsfilter(): a room of wonderful people standup if your age>80 ; age>40 && age <60- 0 <= Number of elements in the Output <= Number of elements in the Input- Input: if you have a Stream<T> then filter takes a Predicate<T>- Takes Predicate as an argument which has test method and returns boolean- Both map & filter stay within their swim lanes, take an element and performs some
kind of operation on it but not looking for elements on the left & right e.g. if I come & ask you your name
- Reduce cut across the swim lanes- Takes the first element coming in, performs the operation, push the result out that
results becomes the input; step forward
Collect The Streams collect(): - 0 <= Number of elements in the Output <= Number of elements in the Input- Input: if you have a Stream<T> then filter takes a Predicate<T>- Takes Predicate as an argument which has test method and returns boolean- Both map & filter stay within their swim lanes, take an element and performs some
kind of operation on it but not looking for elements on the left & right e.g. if I come & ask you your name
- Reduce cut across the swim lanes- Takes the first element coming in, performs the operation, push the result out that
results becomes the input; step forward
Efficiency Of StreamsList<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10); //double the first even number greater than 3 from the list int result = 0; for(int e : numbers) { if(e > 3 && e%2 == 0) { result = e * 2; break; } }System.out.println("Result="+ result); // How does this code looks to you?Spaghetti, Scares the Heck Out of Me
//Note : Imperative code has low level constructs ; hides truth ; waiting to hurt you
Streams to the Rescue !!List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10); //double the first even number greater than 3 from the list System.out.println( numbers.stream() .filter(e -> e >3) // given an element return element >3 ; test 10 elements .filter(e -> e%2==0) // test 7 elements .map(e -> e*2) // test 4 elements .findFirst() // test 1 element .orElse(0) ); //Answer is Optional[8] why Optional[8]? because the given input may not have a even number or number > 3 . In such case i want to display 0 using OrElse which substitutes Optional valueGreat !! What About Performance??//Lets Measure It: 22 vs 8 computations of Imperative – That’s Not the Truth
Truth is Lazy Evaluation Time for Life Lesson: Feature of Streams is characteristically shared by both of my daughters who are Lazy to the book & are doing something in their bedroom having fun fighting with each other and I ask :
Have you done your Homework? - Yes Daddy How did it go? - Fantastic Daddy Are there any concerns? - No we are good Can i see the results please? - And then i dont see them for a long time
So I ask my wife where are the children? Have you asked for the Homework? - My wife says to the Children Daddy is Coming and it triggers Oh dear I got to get some work done here
Streams Are Lazy Too !!• Methods like filter & map are called intermediate functions; they are lazy and dont do much
work whereas findFirst are called terminal functions which trigger the computations
• All intermediate functions get fused into one ball of functions in a pipeline and gets executed only when a terminal function is called.
To prove : comment out findFirst() then print SOP("here") in the next line And guess what it never did the work i.e. intermediate functions never get executed. Its just like a day in office when boss is not around; no work gets done i.e. postpone evaluation and no computation cycles
Demo Time : Lazy Evaluationpublic static boolean isGreaterThan3(int number) { return number > 3; } public static boolean isEven(int number) { return number % 2 == 0; } public static int doubleIt(int number) { return number * 2; } public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); //double the first even number greater than 3 from the list System.out.println( numbers.stream() .filter(Sample::isGreaterThan3) .filter(Sample::isEven) .mapToInt(Sample::doubleIt) .findFirst() .getAsInt() ); }
Don’t’s With Lambda Rulesx Don’t put Multiples Lines Of Code in Lambdax No large lambda expressionsx Avoid Noisex Duplication Is Evil. One Lambda has same code as other
lambdax Logic In Lambda results in Hard to Test. So write the logic
in functions
Method Reference Simple Pass Thru Receive An Argument don’t alter it but simply pass thru Most ultimate glue code Parameter as an Argument: System.out is an Object on which the
println method is called hence System.out::println Parameter as an Argument using Static Method: String::valueOf
Please note valueOf() is a static method on String object whereas println is an instance method on System.out object
Parameter As A Target: String::toString
Reactive Programming In RP: FP++ And parallel streams ability to lazy evaluation ability to communicate a higher level of abstraction
Use case: You call a Web Service which returns a error in place of data and you have to handle the exception in one of your threads
Parallelism
Parallel StreamsJava 8 introduces parallel streamsFork/Join framework abstractionStream partitioned into substreamsAggregation is applied in parallel to substreamsAccess parallel streams via
Collections.parallelStream()BaseStream.parallel()
Parallel ExecutionIntStream.range(0,20) .parallel() .filter( x -> (x % 2) == 0) .forEach(System.out::println);
Ordouble avgDays = months .parallelStream() .mapToInt(m -> m.days) .average().getAsDouble();
Other Language ChangesDefault Methodsjava.util.Optional<T> classAnnotations on Types
Conclusion Two Key Features : invokedynamic & laziness of streams
functions are pure and lets all adapt to functional hygiene and write better quality code. Take a step in right direction, create a Haha moment for yourself when your code reads like a story / problem statement