175
Jumping With Java8 Come, Lambda Along! [email protected] @softwareartisan

Jumping-with-java8

Embed Size (px)

Citation preview

Page 1: Jumping-with-java8

Jumping With Java8

Come, Lambda Along!

[email protected]

@softwareartisan

Page 2: Jumping-with-java8

A Pure FunctionUses nothing other than i/p parameters (and its definition) to produce o/p - Deterministic in nature.

Neither modifies input arguments nor reads/modifies external state - No side-effects.

Call is substitutable by its body. To understand the code, you don’t have to look elsewhere.class Calculator { public Integer add(final Integer x, final Integer y) { return x + y; }}

Page 3: Jumping-with-java8

class Calculator { private int memory = 0; public Calculator(final int memory) { this.memory = memory; } public Integer add(final int x, final int y) { return x + y; } public Integer memoryPlus(final int n) { memory = add(memory, n); return memory; }}

Side-effecting FunctionModifies or interacts with things outside of its scope, and may also return a value.

Outside of its scope

Page 4: Jumping-with-java8

Side-effecting FunctionChanges something somewhere at either class or module or global or at world level.

Performing side-effects like reading or writing to socket/file/db etc…

Throwing an exception and using it to alter the control flow or alter the program state.

This makes it difficult to reason about the program.

Can produce different o/p for same i/p.

Page 5: Jumping-with-java8

A Black-Hole like Function

Always consumes, never returns anything back.

It affects the world by generating a side-effect, example - the setters.

class Calculator { private Integer memory; public void setMemory(final Integer value) { memory = value; }}

Page 6: Jumping-with-java8

A Mother-like FunctionGives unconditionally without asking for anything.

Example - the getters.

class Calculator {

private Integer memory; public Integer recallMemory() { return memory; }}

Page 7: Jumping-with-java8

public String concat(String x, String y) { return x + y;}

String a = "referential";String b = " transparency ";

String r1 = concat(a, b); // referential transparencyString r2 = concat(a, b); // referential transparency

Understanding Referential Transparency

Page 8: Jumping-with-java8

public String concat(StringBuilder x, String y) { return x.append(y).toString();}

StringBuilder a = new StringBuilder("referential");String b = " transparency ";

String r1 = concat(a, b); // referential transparencyString r2 = concat(a, b); // referential transparency referential

Understanding Referential Transparency

Page 9: Jumping-with-java8

class Calculator { private Integer memory; public Calculator(final Integer memory) { this.memory = memory; }

public Integer add(final Integer x, final Integer y) { return x + y; } public Integer memoryPlus(final Integer n) { memory = add(memory, n); return memory; }}

Understanding Referential Transparency

Page 10: Jumping-with-java8

c.add(2, 3); // 5 c.add(2, 4); // 6c.add(2, 3); // 5 c.add(2, 4); // 6

Referential TransparencyCalculator c = new Calculator();

Referentially Opaque memoryPlus : 1.Cannot replace it with resulting value. 2.Returns different results as time

progresses, as behaviour depends on history.

c.memoryPlus(2); // 2c.memoryPlus(3); // 5c.memoryPlus(2); // 7c.memoryPlus(3); // 10

Time Time

Referentially Transparent add : 1.Substitute any expression with its

resulting value. 2. Returns same results all the time, as

behaviour does not depend on history.

Page 11: Jumping-with-java8

How can we make memoryPlusReferential Transparent?

Page 12: Jumping-with-java8

Ref. TransparentmemoryPlus

class Calculator { private final Integer memory; public Calculator(final Integer memory) { this.memory = memory; }

public Integer add { … }

public Calculator memoryPlus(final Integer n) { return new Calculator(add(memory, n)); }}

Make memory Immutable

Return new instance from operation.

Page 13: Jumping-with-java8

ReflectionsReferential Transparency is about replacing any expression (or function) with its resulting value.

Referentially transparent functions are context-free. In our example, the context is time.

Use in different contexts.

Neither alters the meaning of the context.

Nor their behaviour.

Page 14: Jumping-with-java8

Reflections

To be referentially transparent, function will require to work with immutable data.

To be referentially transparent, a function will require to be pure.

Page 15: Jumping-with-java8

Why use Immutability and Pure Functions?Immutablity.

Promotes caching of objects - Flyweights.

Enables concurrent operations.

Pure Functions.

Promote Memoization - caching results of expensive computations.

Page 16: Jumping-with-java8

Order of program evaluation can be changed by compiler to take advantage of multiple cores.

It becomes hard to debug functions with side-effects as program behaviour depends on history.

So, Immutability and Pure functions together make it easier to reason about program.

Page 17: Jumping-with-java8

Chai Chat: OO & FP

Page 18: Jumping-with-java8

Chai Chat: OO & FPWe encapsulate data because we think

it will protect us from inadvertent changes and build trust.

Page 19: Jumping-with-java8

Chai Chat: OO & FPWe encapsulate data because we think

it will protect us from inadvertent changes and build trust.

The data itself is immutable. As data cannot change, trust is inherent.

f

Page 20: Jumping-with-java8

Chai Chat: OO & FPWe encapsulate data because we think

it will protect us from inadvertent changes and build trust.

The data itself is immutable. As data cannot change, trust is inherent.

f

Data (structure) is hidden and the client is not coupled to it.

Page 21: Jumping-with-java8

Chai Chat: OO & FPWe encapsulate data because we think

it will protect us from inadvertent changes and build trust.

The data itself is immutable. As data cannot change, trust is inherent.

f

Data (structure) is hidden and the client is not coupled to it.

If its immutable, why bother encapsulating?

f

Page 22: Jumping-with-java8

Chai Chat: OO & FP

Page 23: Jumping-with-java8

Knowing the innards of an object, causes coupling to parts, which comes in the way of refactoring as requirements change.

Chai Chat: OO & FP

Page 24: Jumping-with-java8

Knowing the innards of an object, causes coupling to parts, which comes in the way of refactoring as requirements change.

Hmm… however an in-place update in OO thru’ methods stores the latest value.

f

Chai Chat: OO & FP

Page 25: Jumping-with-java8

Knowing the innards of an object, causes coupling to parts, which comes in the way of refactoring as requirements change.

Hmm… however an in-place update in OO thru’ methods stores the latest value.

f

This mutation to an OO object makes it hard to reason about its past and therefore its current state. It is easy to miss the point that in OO, state and time are conflated.

f

Chai Chat: OO & FP

Page 26: Jumping-with-java8

Chai Chat: OO & FP

Page 27: Jumping-with-java8

Functions that operate on immutable data are then pure functions. For any transformation they produce new data, leaving the old unmodified.

fChai Chat: OO & FP

Page 28: Jumping-with-java8

Functions that operate on immutable data are then pure functions. For any transformation they produce new data, leaving the old unmodified.

f

Time is never conflated with state, because (immutable) data is a snapshot at a point in time of a particular state.

f

Chai Chat: OO & FP

Page 29: Jumping-with-java8

Hmmm…One can work to make an object immutable though!

Functions that operate on immutable data are then pure functions. For any transformation they produce new data, leaving the old unmodified.

f

Time is never conflated with state, because (immutable) data is a snapshot at a point in time of a particular state.

f

Chai Chat: OO & FP

Page 30: Jumping-with-java8

Encapsulation Vs Open Immutable Data (structures).

Immutability Vs State-Time Conflation.

Don’t we value both?

Immutability

Encapsulation

Reflections

Page 31: Jumping-with-java8

Good Deeds Happen AnonymouslyAn anonymous function - Lambda

Hard to name thingspublic Integer add(final Integer x, final Integer y) { return x + y;}

public Integer add(final Integer x, final Integer y) { return x + y;}

(final Integer x, final Integer y) -> { x + y; }

(x, y) -> x + y;

Drop all inessentials

what remains are essentials,

parameters and body

Page 32: Jumping-with-java8

new Button().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.print("essence"); }});

new Button().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("essence"); }});

new Button().addActionListener(e -> System.out.print("essence"));

Drop all inessentials

what remains are essentials,

parameters and body

SAMs become…

new Button().addActionListener(System.out::print);

OR

Page 33: Jumping-with-java8

LambdaCompiled as interface implementation with synthesised method having signature of the abstract method in that interface.

An interface with single abstract method (SAM).

@FunctionalInterface - Though optional annotation, its better to have it so that it ensures that it stays as a lambda and not become something more.

Function<Integer, Integer> twice = x -> 2 * x;

twice.apply(3); // 6 It would be have been nice if some more syntactic sugar

was added by Java8 to make this happentwice(3);Instead of

Page 34: Jumping-with-java8

Functional Interfaces@FunctionalInterfacepublic interface Function<T, R> { R apply(T t); … }Function<Float, Float> twice = x -> 2 * x;twice.apply(3); // 6

// A function returns its argument unchangedFunction<Float, Float> identity = x -> x@FunctionalInterfacepublic interface BiFunction<T, U, R> { R apply(T t, U u); … }BiFunction<Float, Float, Float> add = (x, y) -> x + y;add.apply(2, 3); // 5

Page 35: Jumping-with-java8

ExistingFunctional Interfaces

public interface Comparator<T> { public int compare(T o1, T o2);}

// can be written as(a, b) -> (a < b) ? -1 : (a == b) ? 0 : 1;

// or simply(a, b) -> a - b;

Page 36: Jumping-with-java8

A Black-Hole like Functional Interface

@FunctionalInterfacepublic interface Consumer<T> { void accept(T t); …}

@FunctionalInterfacepublic interface BiConsumer<T, U> { void accept(T t, U u); …}

Things entering the black hole,

never return back!

Consumer<Float> corrupt = bribe -> { }

BiConsumer<Celestial, Celestial> blackHole = (planet, asteroid) -> { }

corrupt.accept(100.34);

blackHole.accept(planet, asteriod);

Page 37: Jumping-with-java8

Existing Consumers

public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent e);}

// Can be written asevent -> System.out.println(event);

Page 38: Jumping-with-java8

A Mother-like Functional Interface

@FunctionalInterfacepublic interface Supplier<T> { T get();}

Things Emerge without

asking for anything

Supplier<String> mother = () -> "Love";

mother.get(); // Love

Page 39: Jumping-with-java8

Existing Suppliers

public interface Callable<V> { public V call();}

// Can be written as:() -> 2;

Page 40: Jumping-with-java8

Mr.Spock-like Functional Interface

@FunctionalInterfacepublic interface Predicate<T> { boolean test(T t); …}

Predicate<T> affirmative = x -> true; affirmative.test(1); // true

Predicate<T> negative = x -> false; negative.test(1); // false

Predicate<Integer> isEven = x -> x % 2 == 0;isEven.test(2); // trueisEven.test(3); // false

Page 41: Jumping-with-java8

Functional Interface

Unimplemented Method

Static Method

Default Method

1

0..*

0..*

Page 42: Jumping-with-java8

Default & Static MethodsDefault Methods

Helps in Evolving Interfaces.

However, default methods are for default behaviours, and not for shoving in duplicate behaviour.

In other words, don’t abuse it for implementation inheritance and turn it in an implementation class.

Static Methods

Use them as creation methods to create a concrete object of that type.

Page 43: Jumping-with-java8

static Boolean is(final Integer n, String op) { Predicate<Integer> isEven = x -> x % 2 == 0; Map<String, Predicate<Integer>> preds = new HashMap<>(); preds.put("even", isEven); preds.put("odd", isEven.negate());

Predicate<Integer> falsify = x -> false; return preds.getOrDefault(op, falsify).test(n);}

is(5, "even");is(5, "odd");

Defining Function within a function - Encapsulate fns

Java does not allow you to define a function within a function, but its ok defining lambda.

Page 44: Jumping-with-java8

Encapsulate Self-Ref Anonymous Functions?

Integer sum(List<Integer> ns) { BiFunction<Integer, List<Integer>, Integer> sum0 = (acc, xs) -> { if (xs.isEmpty()) return acc; else return sum0.apply(acc + xs.get(0), xs.subList(1, xs.size())); }; return sum0.apply(0, ns);}

Variable ‘sum0’ might have not been initialized!

CompilationBoom!

Page 45: Jumping-with-java8

Encapsulate Self-Ref Anonymous Functions

class Reducer { private static BiFunction<Integer, List<Integer>, Integer> sum0 = (acc, xs) -> { if (xs.isEmpty()) return acc; else return Reducer.sum0.apply(acc + xs.get(0), xs.subList(1, xs.size())); };

Integer sum(List<Integer> ns) { return sum0.apply(0, ns); }}

To self-reference, you will need to…

Page 46: Jumping-with-java8

Every-“Thing” is a Lambda

Function as a Type.

Do we need booleans?

Basic Boolean operations (and, or, not)

https://github.com/CodeJugalbandi/FunctionalProgramming/tree/master/melodies/functionsAreEntities

https://github.com/CodeJugalbandi/FunctionalProgramming/tree/master/melodies/functionsAreTypes

Function as a Data Structure.

Do we need lists?

Do we need integers?

Page 47: Jumping-with-java8

So, like Object…A Function is also a thing, so

Pass a function to a function

Return a function from within a function

A Function that produces or consumes a function is called as Higher Order Function - HOF

Either pass existing method reference (static or instance) or write an in-place lambda where a method expects a function parameter.

Page 48: Jumping-with-java8

void iterate(int times, Runnable body) { if (times <= 0) { return; } body.run(); iterate(times - 1, body);}

iterate(2, () -> System.out.println("Hello"));// Hello// Hello

Pass function to a function

Subsumed ‘for’ loop.

Repetitive behaviour using Recursion.

Simplified iteration without a predicate, but you get the idea.

No need for explicit looping constructs! Just a function!

Page 49: Jumping-with-java8

Function<Double, Double> power(double raiseTo) { return x -> Math.pow(x, raiseTo);}

Function<Double, Double> square = power(2.0);square.apply(2.0); // 4.0

Function<Double, Double> cube = power(3.0);cube.apply(2.0); // 8.0

Return function from a functionSubsumed Factory Method.

Page 50: Jumping-with-java8

Another Exampleclass Maker { }

class Checker { }

public interface Transaction { public boolean approve(); public boolean reject(String reason);}

public interface ApprovalStrategy { boolean approve(Transactions transactions);

public static ApprovalStrategy valueOf(Transactions transactions) { if (transactions.value() < 100000) return new SingleMakerChecker(); else return new DoubleMakerChecker(); }}

Page 51: Jumping-with-java8

Another Exampleclass Maker { }

class Checker { }

public interface Transaction { public boolean approve(); public boolean reject(String reason);}

public interface ApprovalStrategy { boolean approve(Transactions transactions);

public static ApprovalStrategy valueOf(Transactions transactions) { if (transactions.value() < 100000) return new SingleMakerChecker(); else return new DoubleMakerChecker(); }}

Download the Gist Bank Maker-Checker Refactoring

Page 52: Jumping-with-java8

Another Example

class DoubleMakerChecker implements ApprovalStrategy { public DoubleMakerChecker(Maker m, Checker c1, Checker c2) { }

public boolean approve(Transactions ts) { return true; }}

class SingleMakerChecker implements ApprovalStrategy { public SingleMakerChecker(Maker m, Checker c) { }

public boolean approve(Transactions ts) { return true; }}

ApprovalStrategy

SingleMakerChecker DoubleMakerChecker

Page 53: Jumping-with-java8

class Transactions { private final List<Transaction> transactions;

private Transactions(List<Transaction> transactions) { this.transactions = transactions; }

public boolean approve(ApprovalStrategy aps) { return aps.approve(ts); }

public Double totalValue() { // This is hard-coded for purpose of this example. return 1000000d; } }

//mainTransactions transactions = new Transactions(Arrays.asList(…));ApprovalStrategy approvalStrategy = ApprovalStrategy.valueOf(transactions);transactions.approve(approvalStrategy);

Page 54: Jumping-with-java8

class Transactions { private final List<Transaction> transactions;

private Transactions(List<Transaction> transactions) { this.transactions = transactions; }

public boolean approve(ApprovalStrategy aps) { return aps.approve(ts); }

public Double totalValue() { // This is hard-coded for purpose of this example. return 1000000d; } }

//mainTransactions transactions = new Transactions(Arrays.asList(…));ApprovalStrategy approvalStrategy = ApprovalStrategy.valueOf(transactions);transactions.approve(approvalStrategy);

Is there any scope to refactor this code to a better one?

Page 55: Jumping-with-java8

Subsumed Strategyclass Maker { }class Checker { }interface Transaction { public boolean approve(); public boolean reject(String reason);}public class ApprovalStrategy { static Predicate<Transactions, Boolean> valueOf(Transactions transactions) { if (transactions.value() < 100000) { SingleMakerChecker smc = new SingleMakerChecker(…); return smc::approve; } else { DoubleMakerChecker dmc = new DoubleMakerChecker(…); return dmc::approve; }}

class SingleMakerChecker { public SingleMakerChecker(Maker m, Checker c) { } public boolean approve(Transactions ts) { return true; }}

Page 56: Jumping-with-java8

class Transactions { private final List<Transaction> transactions; private Transactions(List<Transaction> transactions) { this.transactions = transactions; } public boolean approve(Predicate<Transactions> aps) { return aps.test(ts); } public Double totalValue() { // This is hard-coded for purpose of this example. return 1000000; } }//mainTransactions transactions = new Transactions(Arrays.asList(…));transactions.approve(ApprovalStrategy.valueOf(transactions));

class DoubleMakerChecker { public DoubleMakerChecker(Maker m, Checker c1, Checker c2) { }

public boolean approve(Transactions ts) { return true; }}

Subsumed Strategy

Page 57: Jumping-with-java8

abstract class Logger { enum Level { INFO, WARN, ERROR, FATAL }; public void log(Level level, String message) { String logMessage = enrich(level, message); write(logMessage); } // Hook protected String enrich(Level level, String message) { return String.format("%s [%s] %s %s", new Date(), Thread.currentThread(), level, message); } //Mandate abstract void write(String message);}class DatabaseLogger extends Logger { DatabaseLogger(String url) { } void write(String message) { System.out.println("Database Logger writing => " + message); }}class ConsoleLogger extends Logger { void write(String message) { System.out.println("Console Logger writing => " + message); }}

Can the design be improved?

Download the Gist Logger Refactoring

Page 58: Jumping-with-java8

Subsumed Templateclass Logger { private final Consumer<String> destination; enum Level { INFO, WARN, ERROR, FATAL };

Logger(Consumer<String> destination) { this.destination = destination; } public void log(Level level, String message) { String logMessage = enrich(level, message); destination.accept(logMessage); } protected String enrich(Level level, String message) { return String.format("%s [%s] %s %s", new Date(), Thread.currentThread(), level, message); }}class DatabaseWriter { DatabaseWriter(String url) { } void write(String message) { System.out.println("Database Logger writing => " + message); }}public static void main(String[] args) throws Exception { Logger db = new Logger(new DatabaseWriter("url")::write); db.log(Logger.Level.INFO, "Hello"); Logger console = new Logger(System.out::println); console.log(Logger.Level.INFO, "Hello");}

Page 59: Jumping-with-java8

Subsumed ProxyMemoization

public<T, R> Function<T, R> memoize(Function<T, R> fn) { final Map<T, R> cache = new ConcurrentHashMap<>(); return t -> { if (!cache.containsKey(t)) { R r = fn.apply(t); cache.put(t, r); return r; } return cache.get(t); };}Function<Double, Double> memoizedDoubler = memoize(x -> { System.out.println("Evaluating..."); return x * 2;});memoizedDoubler.apply(2.0); // Evaluating…memoizedDoubler.apply(2.0);memoizedDoubler.apply(3.0); // Evaluating…memoizedDoubler.apply(3.0);

Page 60: Jumping-with-java8

Look Ma! Its Raining Lambdas

Subsumed Aspect - AOP around style.

public<T, R> Function<T, R> time(Function<T, R> fn) { return t -> { long startTime = System.currentTimeMillis(); R result = fn.apply(t); long timeTaken = System.currentTimeMillis() - startTime; System.out.println("Time: " + timeTaken + " ms"); return result; }}

Page 61: Jumping-with-java8

Subsumed DecoratorFunction<Integer, Integer> expensiveSquare = x -> { System.out.println("Now Squaring..."); try { Thread.sleep(2 * 1000); } catch (Exception e) { } return x * x;};

// Decorate with time and memoizeFunction<Integer, Integer> tmSquare = time(memoize(expensiveSquare));System.out.println(tmSquare.apply(3));System.out.println(tmSquare.apply(3));

With the ability to pass and return funcs, OO design patterns like Strategy, Proxy, Decorator etc… get subsumed in FP.

Page 62: Jumping-with-java8

Can

this

be

impr

oved

?

class Sql { static List<Employee> execute(String dburl, String sql) { Connection connection = null; Statement statement = null; ResultSet resultSet = null; List<Employee> employees = new ArrayList<Employee>(); try { connection = DriverManager.getConnection(dburl); statement = connection.createStatement(); statement.execute(sql); resultSet = statement.getResultSet(); while (resultSet.next()) { int empId = resultSet.getInt(0); String name = resultSet.getString(1); employees.add(new Employee(empId, name)); } } catch (SQLException e) { e.printStackTrace(); } finally { if (connection != null) { connection.close(); if (statement != null) { statement.close(); if (resultSet != null) resultSet.close(); } } } return employees; }}

Download the Gist Smelly JDBC Code

Page 63: Jumping-with-java8

Loan My Resourceinterface ConsumerThrowsException<T, E extends Throwable> { public void accept(T t) throws E;}class Sql { static<T> void execute(String dburl, String sql, ConsumerThrowsException<ResultSet, SQLException> fn) throws SQLException { try (Connection connection = DriverManager.getConnection(dburl)) { try (Statement statement = connection.createStatement()) { statement.execute(sql); try (ResultSet resultSet = statement.getResultSet()) { fn.accept(resultSet); } } } catch (SQLException e) { throw e; } }}Sql.execute(dburl, "select * from events limit 1", resultSet -> { //loop through result set and map to List<Event> });

Page 64: Jumping-with-java8

Loan My Resource

Acquire

Loan resource for use

Release

Page 65: Jumping-with-java8

Functions as a part of data structures

List<BiFunction<Integer, Integer, Integer>> operations = new ArrayList<BiFunction<Integer, Integer, Integer>>() {{ add((x, y) -> x + y); add((x, y) -> x * y); add((x, y) -> x - y);}};

int x = 2, y = 3;for (BiFunction<Integer, Integer, Integer> op : operations) { System.out.println(op.apply(x, y));}

Page 66: Jumping-with-java8

Imperative Collecting and Filtering

String sentence = "all mimsy were the borogoves and the momeraths";String [] words = sentence.split(" ");StringBuilder caps = new StringBuilder();

for (word : words) { if (word.length() < 4) { caps.append(word.toUpperCase()); caps.append(" "); }}

String capitalized = caps.toString().trim();System.out.println(capitalized); // ALL THE AND THE

Enumeration and

Filtering interleaved

Collector

Page 67: Jumping-with-java8

In Java8…In-place mutation is a standing invitation

Its hard to avoid falling into that trap.

One has to work hard to bring immutability.

Use ‘final’ wherever possible.

Use Expressions wherever possible

Statements effect change by mutation and thus encourage mutability

Expressions evaluate to return values and thus encourage immutability

Page 68: Jumping-with-java8

Refactored CodeList<String> split(String sentence) { return Arrays.asList(sentence.split(" "));}List<String> capitalize(List<String> words) { List<String> upperCased = new ArrayList<String>(); for (word : words) { upperCased.add(word.toUpperCase()); } return upperCased;}List<String> lessThan4(List<String> words) { List<String> filtered = new ArrayList<String>(); for (word : words) { if (word.length() < 4) { filtered.add(word); } } return filtered;}

Page 69: Jumping-with-java8

Refactored CodeString join(final List<String> words) { StringBuilder joined = new StringBuilder(); for (word : words) { joined.append(" "); } return joined.toString().trim();}

String sentence = "all mimsy were the borogoves and the mome raths";String capitalized = join(capitalize(lessThan4(split(sentence))));System.out.println(capitalized); // ALL THE AND THE

Page 70: Jumping-with-java8

Additional Scenarios to consider

What about large data-set (memory concern)?

If I need only first few elements, then why do I need to go through every element? Can I not short-circuit the processing?

Pros Cons

No mutation and SRPier functions

End-up creating many intermediate lists in the

call chain.

Improved ReadabilityWhat about memory utilisation and performance?

Page 71: Jumping-with-java8

Enter StreamsCollection where you access elements one by one - amortised list.

Generating Stream that is backed by finite elements - of

Stream<Integer> primes = Stream.of(2, 3, 5, 7, 9);

Generating a Stream backed by list.

Stream<Integer> primes = Arrays.asList(2, 3, 5, 7, 9) .stream();

Page 72: Jumping-with-java8

Sequencing

Stream.of("Brahma", "Vishnu", "Mahesh") .map(String::toUpperCase) .forEach(System.out::println); // BRAHMA // VISHNU // MAHESH

Chaining and applying operations one after the other.

Moving data down the processing pipes.

Operate on collection elements one at a time, rather than operate on all elements at once.

Page 73: Jumping-with-java8

Stream OperationsIterate each element (Terminal).

forEach

Transform each element (Non-Terminal).

map, flatMap

Retrieve Elements that satisfy certain criterion

findFirst, filter, allMatch, anyMatch, noneMatch

Debug each element.

peek

Page 74: Jumping-with-java8

Stream Ops

Combine adjacent elements

reduce, min, max, count, sum

Sort and Unique

sorted and distinct

take and drop

limit, skip

Page 75: Jumping-with-java8

Parallel StreamDouble expensiveSquare(Double n) { try { Thread.sleep(2 * 1000); // 2 secs return n * n; } catch (InterruptedException ie) { return Double.NaN; }}

List<Double> timer(Stream<Double> numbers) { long startTime = System.currentTimeMillis(); List<Double> squares = numbers.map(n -> expensiveSquare(n)) .collect(Collectors.toList()); long timeTaken = System.currentTimeMillis() - startTime; System.out.println("Time: " + timeTaken + " ms"); return squares;}

List<Double> numbers = Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0);timer(numbers.stream()); // Time: 10078 mstimer(numbers.parallelStream()); // Time: 2013 ms

Page 76: Jumping-with-java8

Parallel Stream andSide-Effects

String sentence = "all mimsy were the borogoves and the mome raths";

// Prints in the order the stream encounteredsentence.chars() .forEach(ch -> System.out.println((char) ch));

// Does not print in the order the stream encountered// If it prints in order you are just “lucky”! Try running again…sentence.chars() .parallel() .forEach(ch -> System.out.println((char) ch));

// Prints in the order the stream encounteredsentence.chars() .parallel() .forEachOrdered(ch -> System.out.println((char) ch));

Page 77: Jumping-with-java8

Compilation Boom!!“local variables referenced from a lambda expression must be final or effectively

final” .forEach(word -> capitalized += word);

StatefulEffectively final or final

String sentence = "all mimsy were the borogoves and the mome raths";

String capitalized = "";

Stream.of(sentence.split(" ")) .filter(w -> w.length() < 4) .map(String::toUpperCase) .forEach(word -> capitalized += word);

Page 78: Jumping-with-java8

You get clever!String sentence = "all mimsy were the borogoves and the mome raths";StringBuilder capitalized = new StringBuilder();

Stream.of(sentence.split(" ")) .filter(w -> w.length() < 4) .map(String::toUpperCase) .forEach(word -> capitalized.append(word).append(" "));

System.out.println(capitalized.toString().trim()); // ALL THE AND THE

Page 79: Jumping-with-java8

You get clever!String sentence = "all mimsy were the borogoves and the mome raths";StringBuilder capitalized = new StringBuilder();

Stream.of(sentence.split(" ")) .filter(w -> w.length() < 4) .map(String::toUpperCase) .forEach(word -> capitalized.append(word).append(" "));

System.out.println(capitalized.toString().trim()); // ALL THE AND THE

Now try this!String sentence = "all mimsy were the borogoves and the mome raths";StringBuilder capitalized = new StringBuilder();

Stream.of(sentence.split(" ")) .parallel() .filter(w -> w.length() < 4) .map(String::toUpperCase) .forEach(word -> capitalized.append(word).append(" "));

System.out.println(capitalized.toString().trim()); // THE AND ALL THE

Page 80: Jumping-with-java8

Stateful +

Parallel==

Problem!

Page 81: Jumping-with-java8

Stream OpsMutable Reduction

collect - in which the reduced value is a mutable result container (like ArrayList) and elements are incorporated by updating the state of the result rather than by replacing the result.

collect using Collectors

toList, toSet etc…

maxBy, minBy, groupingBy, partitioningBy

mapping, reducing etc…

Page 82: Jumping-with-java8

Stateless…String sentence = "all mimsy were the borogoves and the mome raths";String capitalized = Stream.of(sentence.split(" ")) .filter(w -> w.length() < 4) .map(String::toUpperCase) .collect(Collectors.joining(" "));

System.out.println(capitalized); // ALL THE AND THE

String sentence = "all mimsy were the borogoves and the mome raths";String capitalized = Stream.of(sentence.split(" ")) .parallel() .filter(w -> w.length() < 4) .map(String::toUpperCase) .collect(Collectors.joining(" "));

System.out.println(capitalized); // ALL THE AND THE

…and Parallel

Page 83: Jumping-with-java8

Stateless +

Parallel==

No Problem!

Page 84: Jumping-with-java8

Eager EvaluationJava uses eager evaluation for method arguments.

Args evaluated before passing to the method.

class Eager<T> { private final T value; Eager(final T value) { System.out.println("eager..."); this.value = value; }

public T get() { return value; }}

Integer twice(Integer n) { System.out.println("twice..."); return 2 * n;}

Eager<Integer> eager = new Eager(twice(3));// twice…// eager…

System.out.println(eager.get());// 6

Page 85: Jumping-with-java8

Simulate Lazy EvaluationLazy - Don’t compute until demanded for.

To delay the evaluation of args (lazy), wrap them in lambda.

Call the lambda when we need to evaluate.class Lazy<T> { private final Supplier<T> value; Lazy(final Supplier<T> value) { System.out.println("lazy..."); this.value = value; } public T get() { return value.get(); }}

Lazy<Integer> lazy = new Lazy(() -> twice(3));// lazy…

System.out.println(lazy.get());// twice…// 6 Representation of

computation and not the

computation itself.

Page 86: Jumping-with-java8

Generating StreamsStream<Integer> naturals(int from) { if (from < 0) throw new IllegalArgumentException(); // By one more than the one before return Stream.iterate(from, x -> x + 1);}

naturals(0).limit(3).forEach(System.out::println); // 0 1 2

Using iterate

Using generateStream<Integer> randoms() { final Random random = new Random(); return Stream.generate(() -> random.nextInt(6)).map(x -> x + 1);}

randoms().limit(3).forEach(System.out::println); // 0 1 2

Page 87: Jumping-with-java8

Infinitely Lazy

Stream<Integer> naturals(int from) { return Stream.iterate(from, x -> x + 1);}// will not terminate naturals(0).forEach(System.out::println);

Streams, unlike lists (finite), are infinite.

They have a starting point, but no end.

Streams, unlike lists (eager), are lazy.

Page 88: Jumping-with-java8

Infinitely Lazy

Immutability and Purity makes lazy evaluation possible.

The answer will be same at time t = 0 and at t = 10 as well.

Immutability and Purity are the key to Laziness.

Page 89: Jumping-with-java8

Lazy Evaluation and Side-Effects

On the other hand, if you mutate (doing side-effects), you will get different answers later, so you have to be eager, you cannot afford to be lazy.

In presence of side-effects, knowing the order is a must.

Lazy-evaluation and side-effects can never be together! It will make programming very difficult.

Page 90: Jumping-with-java8

Virtues of LazinessWith Streams, only essential space is allocated upon materialization, the rest is in ether :)

This reduces memory footprint (as you don’t bring every item in memory).

A powerful modularization: Separating Generation from Selection - John Hughes

This saves CPU cycles (as computation is delayed until demand is placed).

Streams are pull-based, consumer decides the pace of pull as producer is lazy.

Page 91: Jumping-with-java8

Finite from InfiniteList<Integer> evens(int from, int howMany) { return naturals(from) .filter(n -> n % 2 == 0) .limit(howMany) .collect(Collectors.toList());}

List<Integer> first5Evens = evens(0, 5);System.out.println(first5Evens);

Terminal operations like forEach, collect, reduce etc… place demand; whereas non-terminal operations like filter, map, skip etc… return another Stream.

Laziness makes it easy to compose programs as we don’t do more work than essential.

Page 92: Jumping-with-java8

Given two lists:

[‘a’, ‘b’] and [1, 2]

Generate the combinations given below:

[[‘a’, 1], [‘a’, 2], [‘b’, 1], [‘b’, 2]]

Imperative SolutionList<Character> alphabets = Arrays.asList('a', 'b');List<Integer> numbers = Arrays.asList(1, 2); List<List<?>> combinations = new ArrayList<>();for (Character c : alphabets) { for (Integer n : numbers) { combinations.add(Arrays.asList(c, n)); }}

System.out.println(combinations); // [[a, 1], [a, 2], [b, 1], [b, 2]]

Page 93: Jumping-with-java8

Subsumed Nested LoopsList<List<?>> combinations(List<?> one, List<?> two) { return one.stream() .flatMap(o -> two.stream().map(t -> Arrays.asList(o, t))) .collect(Collectors.toList());}

List<Character> alphabets = Arrays.asList('a', 'b');List<Integer> numbers = Arrays.asList(1, 2); List<List<?>> combo = combinations(alphabets, numbers);

System.out.println(combo); // [[a, 1], [a, 2], [b, 1], [b, 2]]

Page 94: Jumping-with-java8

Using SpliteratorSpliterators, like Iterators, are for traversing the elements of a source.

Supports efficient parallel traversal in addition to sequential traversal.

It splits the collection and partitions elements. By itself, this not parallel processing, its simply division of data.

Not meant for mutable data-sources.

Non-deterministic behaviour when the data-source is structurally modified (add/remove/update elements) during the traversal.

https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html

Page 95: Jumping-with-java8

Streamable ResultSetclass ResultSetIterator<T> implements Iterator<T> { private ResultSet resultSet; private FunctionThrowsException<ResultSet, T, SQLException> mapper; ResultSetIterator(ResultSet resultSet, FunctionThrowsException<ResultSet, T, SQLException> mapper) { this.resultSet = resultSet; this.mapper = mapper; } @Override public boolean hasNext() { try { return resultSet.next(); } catch (SQLException e) { throw new RuntimeException(e); } } @Override public T next() { try { return mapper.apply(resultSet); } catch (SQLException e) { throw new RuntimeException(e); } } }

Page 96: Jumping-with-java8

interface FunctionThrowsException<T, R, E extends Throwable> { public R apply(T t) throws E;}class Sql { static void execute(String dburl, String sql, FunctionThrowsException<ResultSet, R, SQLException> mapper, Consumer<Stream<R>> consumer) throws SQLException { try (Connection connection = DriverManager.getConnection(dburl)) { try (Statement statement = connection.createStatement()) { statement.execute(sql); try (ResultSet resultSet = statement.getResultSet()) { Iterator rsi = new ResultSetIterator(resultSet, mapper); Stream<R> stream = StreamSupport.stream( Spliterators.spliteratorUnknownSize(rsi, Spliterator.ORDERED), false); consumer.accept(stream); } } } catch (SQLException e) { throw e; } }}

Page 97: Jumping-with-java8

Streamable ResultSet

class Employee { private …,}

Sql.execute(dburl, "select * from employee limit 1", rSet -> new Employee(rSet.getString(1), rSet.getDate(2)), (Stream<Employee> es) -> { // You can now use the employee stream here and // go on pulling the records from database.});

Page 98: Jumping-with-java8

Composition

Function<Integer, Integer> square = x -> x * x;

Function<Integer, Integer> twice = x -> 2 * x;

Function<T, R> compose(Function<U, R> f, Function<T, U> g) { return x -> f.apply(g.apply(x));}

Function<Integer, Integer> twiceAndSquare = compose(square, twice);

twiceAndSquare.apply(2); // 16

Compose a function from other functions by aligning types.

Page 99: Jumping-with-java8

Composition

square.andThen(twice).apply(2); // 8

Java provides composition on Function.

f ⨁ g

Function composition is not commutative.

f ⨁ g != g ⨁ f

square.compose(twice).apply(2); // 16

Page 100: Jumping-with-java8

Composition

Function<String, Stream<String>> split = s -> Stream.of(s.split(" "));

Function<Stream<String>, Stream<String>> capitalize = words -> words.map(String::toUpperCase);

Function<Stream<String>, Stream<String>> lessThan4 = words -> words.filter(word -> word.length() < 4);

Function<Stream<String>, String> join = words -> words.collect(Collectors.joining(" "));

Function<String, String> composedSequence = join.compose(lessThan4).compose(capitalize).compose(split);

composedSequence.apply("all mimsy were the borogoves"); // ALL THE

Composing behaviours…

Earlier, we saw…String sentence = "all mimsy were the borogoves";join(lessThan3(capitalize(split(sentence)))); // ALL THE

Page 101: Jumping-with-java8

Function<String, String> composedSequence =

join.compose(lessThan4).compose(capitalize).compose(split);

composedSequence.apply("all mimsy were the borogoves"); // ALL THE

Function<String, String> andThenedSequence =

split.andThen(capitalize).andThen(lessThan4).andThen(join);

andThenedSequence.apply("all mimsy were the borogoves"); // ALL THE

For languages that support function composition, look for a way to go with the grain of thought.

In Java8, prefer using andThen

Composition

Think Right to Left

Read Left to Right

Read Left to Right

Think Left to Right

Page 102: Jumping-with-java8

Why Composition?Tackle complexity by composing behaviours.

Enforce order of evaluation.

In imperative programming, statements enforce order of evaluation.

In FP, the order of composition determines the order of evaluation.

Page 103: Jumping-with-java8

ReflectionsFunction composition (and not function application) is the default way to build sub-routines in Concatenative Programming Style, a.k.a Point Free Style.

Functions neither contain argument types nor names, they are just laid out as computation pipeline.

Lot of our domain code is just trying to do this!

Makes code more succinct and readable.http://codejugalbandi.github.io/codejugalbandi.org

Page 104: Jumping-with-java8

ReflectionsComposition is the way to tackle complexity - Brian Beckman.

Compose larger functions from smaller ones

Subsequently every part of the larger function can be reasoned about independently.

If the parts are correct, we can then trust the correctness of the whole.

Page 105: Jumping-with-java8

Outside WorldSide

EffectingFunctions

Pure Functions

Compose behaviours using pure Functions.

Data flows through composed pipes.

Interact with outside world using side-effecting functions or Monads.

Circle of Purity

Courtesy: Venkat

Page 106: Jumping-with-java8

Currying

// Function with all args applied at the same time.BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;// Curried Function - one arg at a time.Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;add.apply(2).apply(3); // 5

Unavailable out-of-box in Java8.

Curried function is a nested structure, just like Russian dolls, takes one arg at a time, instead of all the args at once.

For each arg, there is another nested

function, that takes a arg and returns a function taking the

subsequent arg, until all the args are

exhausted.

Page 107: Jumping-with-java8

Why Currying?Helps us reshape and re-purpose the original function by creating a partially applied function from it.

Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;

Function<Integer, Integer> increment = add.apply(1);increment.apply(2); // 3

Function<Integer, Integer> decrement = add.apply(-1);decrement.apply(2); // 1

Page 108: Jumping-with-java8

Why Currying?Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;

Brings in composability at argument level.

Scope Contour 1

Scope Contour 2

Facilitates decoupling of arguments to different scopes.

When ‘increment’ is expressed in terms of ‘add’ where ‘x’ is 1; ‘y’ can come from completely different scope.

Uncurried functions (all Java fns) are not composable at arg-level.

Page 109: Jumping-with-java8

Currying

Function<Integer, Function<Integer, Function<Integer, Integer>>> add = x -> y -> z -> x + y + z;

Function type associates towards right.

In other words, arrow groups towards right.

x -> y -> z -> x + y + z;

// is same asx -> y -> (z -> x + y + z);

// is same asx -> (y -> (z -> x + y + z));

Page 110: Jumping-with-java8

CurryingFunction application associates towards left.

add.apply(2).apply(3).apply(4); // 9

// is same as(add.apply(2)).apply(3).apply(4); // 9

// is same as((add.apply(2)).apply(3)).apply(4); // 9

In other words, apply() groups towards left.

Page 111: Jumping-with-java8

But, how does that still make

programming spicier?

Page 112: Jumping-with-java8

Let’s say we have a Customer repositoryclass CustomerRepository { public Customer findById(Integer id) { if (id > 0) return new Customer(id); else throw new RuntimeException("Customer Not Found"); } }

Now, we want to allow authorised calls to repo. So, Let’s write an authorise function.

class Authoriser { public Customer authorise(CustomerRepository rep, Request req) { //Some auth code here which guards the request. return rep.findById(req.get()); } }

Page 113: Jumping-with-java8

Let’s see them in action…CustomerRepository repo = new CustomerRepository();Authoriser authoriser = new Authoriser();

Request req1 = new Request();Customer customer1 = authoriser.authorise(repo, req1);

Request req2 = new Request();Customer customer2 = authoriser.authorise(repo, req2);

Requests vary, however the CustomerRepository is same.

Can we avoid repeated injection of the repo?

Page 114: Jumping-with-java8

One way is to wrap the authorise function in another function (also called authorise) that consumes Request and produces Customer.

It internally newifies the repository and hard-wires it to the original authorise.

Solution 1

Customer authorise(Request req) { CustomerRepository repo = new CustomerRepository(); return repo.findById(req.get());}

Authoriser authoriser = new Authoriser();Request req1 = new Request();Customer customer1 = authoriser.authorise(req1);Request req2 = new Request();Customer customer2 = authoriser.authorise(req2);

Page 115: Jumping-with-java8

One way is to wrap the authorise function in another function (also called authorise) that consumes Request and produces Customer.

It internally newifies the repository and hard-wires it to the original authorise.

Solution 1

Customer authorise(Request req) { CustomerRepository repo = new CustomerRepository(); return repo.findById(req.get());}

Authoriser authoriser = new Authoriser();Request req1 = new Request();Customer customer1 = authoriser.authorise(req1);Request req2 = new Request();Customer customer2 = authoriser.authorise(req2);

But newification locally like this is

untestable!

Page 116: Jumping-with-java8

One way is to wrap the authorise function in another function (also called authorise) that consumes Request and produces Customer.

It internally newifies the repository and hard-wires it to the original authorise.

Solution 1

Customer authorise(Request req) { CustomerRepository repo = new CustomerRepository(); return repo.findById(req.get());}

Authoriser authoriser = new Authoriser();Request req1 = new Request();Customer customer1 = authoriser.authorise(req1);Request req2 = new Request();Customer customer2 = authoriser.authorise(req2);

But newification locally like this is

untestable!

Reject

Page 117: Jumping-with-java8

CustomerRepository repo = new CustomerRepository();Function<Request, Customer> curriedAuthorise = authorise(repo);

Request req1 = new Request();Customer customer1 = curriedAuthorise.apply(req1);

Request req2 = new Request();Customer customer2 = curriedAuthorise.apply(req2);

class Authoriser { public Function<Request, Customer> authorise(CustomerRepository repo) { //Some auth code here which guards the request. return req -> repo.findById(req.get()); } }

Re-shape authorise to accept only one fixed parameter - CustomerRepository

Solution 2

Page 118: Jumping-with-java8

CustomerRepository repo = new CustomerRepository();Function<Request, Customer> curriedAuthorise = authorise(repo);

Request req1 = new Request();Customer customer1 = curriedAuthorise.apply(req1);

Request req2 = new Request();Customer customer2 = curriedAuthorise.apply(req2);

class Authoriser { public Function<Request, Customer> authorise(CustomerRepository repo) { //Some auth code here which guards the request. return req -> repo.findById(req.get()); } }

Re-shape authorise to accept only one fixed parameter - CustomerRepository

Solution 2

Accept

Page 119: Jumping-with-java8

Solution 3Making our own curry

<T, U, R> Function<T, Function<U, R>> curry(BiFunction<T, U, R> fn) { return t -> u -> fn.apply(t, u);}

// Function with all args applied at the same time.BiFunction<Integer, Integer, Integer> add = (x, y) -> x + y;

// Curried Function - one arg at a time.Function<Integer, Function<Integer, Integer>> cAdd = curry(add);

Function<Integer, Integer> increment = cAdd.apply(1);increment.apply(2); // 3

Function<Integer, Integer> decrement = cAdd.apply(-1);decrement.apply(2); // 1

It would be nice if Java8 provided this

out-of-box on BiFunction

Scala calls this curried

Page 120: Jumping-with-java8

class Authoriser { public Customer authorise(CustomerRepository rep, Request req) { //Some auth code here which guards the request. return repo.findById(req.get()); } }

Parameterize CustomerRepository instead.

CustomerRepository repo = new CustomerRepository();Function<Request, Customer> curriedAuthorise = curry(Authoriser::authorise).apply(repo);

Request req1 = new Request();Customer customer1 = curriedAuthorise.apply(req1);

Request req2 = new Request();Customer customer2 = curriedAuthorise.apply(req2);

Solution 3Making our own curry

Page 121: Jumping-with-java8

class Authoriser { public Customer authorise(CustomerRepository rep, Request req) { //Some auth code here which guards the request. return repo.findById(req.get()); } }

Parameterize CustomerRepository instead.

CustomerRepository repo = new CustomerRepository();Function<Request, Customer> curriedAuthorise = curry(Authoriser::authorise).apply(repo);

Request req1 = new Request();Customer customer1 = curriedAuthorise.apply(req1);

Request req2 = new Request();Customer customer2 = curriedAuthorise.apply(req2);

Solution 3Making our own curry

Accept

Page 122: Jumping-with-java8

ObservationsWe don’t have to provide all the arguments to the function at one go! This is partially applying the function.

In other words, currying enables Partial Function Application, a.k.a - Partially Applied Function (PFA).

NOTE: Partially Applied Function (PFA) is completely different from Partial Function.

Page 123: Jumping-with-java8

Uncurry back or Tuple it!

<T, U, R> BiFunction<T, U, R> uncurry(Function<T, Function<U, R>> fn) { return (t, u) -> fn.apply(t).apply(u);}

// Curried Function - one arg at a time.Function<Integer, Function<Integer, Integer>> add = x -> y -> x + y;

// Function with all args applied at the same time.BiFunction<Integer, Integer, Integer> ucAdd = uncurry(add);

ucAdd.apply(2, 3); // 5

It would be nice if Java8 provided this

out-of-box on Function

Scala calls this tupled

Page 124: Jumping-with-java8

Want More Spice?In the last example, we saw how currying decouples function arguments to facilitate just-in-time dependency injection.

How about constructor or setter dependency injection?

Lets see how currying acts as a powerful decoupler, not just limited to the site function arguments (at least in OO languages).

Page 125: Jumping-with-java8

Regular DIinterface Transaction { }interface ApprovalStrategy { boolean approve(List<Transaction> ts); //…}class Clearing { private final ApprovalStrategy aps;

Clearing(ApprovalStrategy aps) { this.aps = aps; }

public boolean approve(List<Transaction> ts) { return aps.approve(ts); } }//mainApprovalStrategy singleMakerChecker = new SingleMakerChecker();Clearing clearing = new Clearing(singleMakerChecker);clearing.approve(ts);

Page 126: Jumping-with-java8

Curried DIinterface Transaction { }interface ApprovalStrategy { boolean approve(List<Transaction> ts); //…}class Clearing {

public Function<ApprovalStrategy, Boolean> approve(List<Transaction> ts) { return aps -> aps.approve(ts); } }//mainClearing clearing = new Clearing();// ApprovalStrategy can now be injected from different contexts,// one for production and a different one - say mock for testing,// Just like in case of Regular DI.clearing.approve(ts).apply(new SingleMakerChecker());

Page 127: Jumping-with-java8

Refl

ecti

ons

Currying refers to the phenomena of rewriting a N-arg function to a nest of functions, each taking only 1-arg at a time.

It replaces the need for having to explicitly “wrap” the old function with a different argument list - Keeps code DRY.

You curry strictly from left-to-right.

DI is achieved, not just by injecting functions, but also by currying functions. When we curry arguments, we are injecting dependency.

http://codejugalbandi.github.io/codejugalbandi.org

Page 128: Jumping-with-java8

Make Absence Explicit Optional<T>

Stop null abuse!

Don’t return null, use Optional<T> so that it explicitly tells that in the type.

A container or view it as a collection containing single value or is empty.

Presence

Absence

Input(s)

Value

null

Page 129: Jumping-with-java8

class Event { private final Date occurredOn; private final Optional<String> type; public Event(final String type) { occurredOn = new Date(); this.type = Optional.ofNullable(type); }

public Date occurredOn() { return occurredOn; } public Optional<String> type() { return type; }}

Event diwali = new Event("Holiday");System.out.println(diwali.type().get()); // Holiday

Create Optional<T> - of, ofNullable

Get the value back -> get()

Page 130: Jumping-with-java8

Boom!

Set Sensible DefaultAvoid get() - it throws Exception for absence of value

Instead use orElse, orElseGet, orElseThrowEvent shopping = new Event(null);

System.out.println(shopping.type() .orElse("Other")); // OtherSystem.out.println(shopping.type() .orElseGet(() -> "Other"));// Othershopping.type().orElseThrow(() -> new IllegalArgumentException("Empty or null")));

Event shopping = new Event(null);

System.out.println(shopping.type().get());

Page 131: Jumping-with-java8

Do side-effects if value is present

Imperative check for presence of value.

Instead use ifPresent()

Event diwali = new Event("Holiday");

if (diwali.type().isPresent()) { System.out.println(diwali.type().get()); // Holiday}

Event diwali = new Event("Holiday");

diwali.type().ifPresent(System.out::println); // Holiday

Page 132: Jumping-with-java8

Optional Operations

Event diwali = new Event("Holiday");

diwali.type() .map(String::length) // Optional<Integer> .ifPresent(System.out::println); // 7

Transforming Optional - map

Event diwali = new Event("Holiday");

diwali.type() .filter(t -> t.equalsIgnoreCase("holiday")) .ifPresent(System.out::println); // Holiday

Filtering an Optional

Page 133: Jumping-with-java8

Gets rid of nested Optionals

class EventRepository { private final Map<Integer, Event> events = new HashMap<>(); public EventRepository() { events.put(1, new Event("Personal")); events.put(2, new Event("Work")); } public Optional<Event> findById(final Integer id) { return Optional.ofNullable(events.get(id)); }}

Optional flatMap

EventRepository repository = new EventRepository();repository.findById(1) .map(Event::type) // Optional<Optional<String>> .ifPresent(System.out::println);repository.findById(1) .flatMap(Event::type) // Optional<String> .ifPresent(System.out::println);repository.findById(1) .flatMap(Event::type) // Optional<String> .map(t -> String.format("{'type': '%s'}", t)) // Map to Json .orElse("{'error' : 'No Event Found'}"); // Default Json

Page 134: Jumping-with-java8

Checked Exceptions+

Lambda==

Pain!

Page 135: Jumping-with-java8

Option 1: Bubble up…Re-throw the exception as unchecked exception.

String capitalize(String s) throws Exception { if (null == s) throw new Exception("null"); return s.toUpperCase();}

Arrays.asList("Hello", null).stream() .map(s -> { try { return capitalize(s); } catch (Exception e) { throw new RuntimeException(e); } }) .forEach(System.out::println);

Lambda looks grotesque.

Page 136: Jumping-with-java8

Option 2: Deal with it…Handle the exception in the lambda

String capitalize(String s) throws Exception { if (null == s) throw new Exception("null"); return s.toUpperCase();}

Arrays.asList("Hello", null).stream() .map(s -> { try { return capitalize(s); } catch (Exception e) { return "#fail"; } }) .collect(Collectors.toList()); // [HELLO, #fail]

1.Success and Failure are indistinguishable, despite a #fail

2.Lambda still looks grotesque.

Page 137: Jumping-with-java8

Option 3: Wrap using Exceptional SAM

http://mail.openjdk.java.net/pipermail/lambda-dev/2013-January/007662.html

@FunctionalInterfaceinterface FunctionThrowsException<T, R, E extends Throwable> { public R apply(T t) throws E;}

abstract class Try { public static<T, R, E extends Throwable> Function<T, R> with(FunctionThrowsException<T, R, E> fte) { return t -> { try { return fte.apply(t); } catch(Throwable e) { throw new RuntimeException(e); } }; }}

Page 138: Jumping-with-java8

Beauty of Lambda restored.

class Test { String capitalize(String s) throws Exception { if (null == s) throw new Exception("null"); return s.toUpperCase(); }

public static void main(String[] args) { Arrays.asList("Hello", null) .stream() .map(s -> Try.with(Test::capitalize).apply(s)) .forEach(System.out::println); }}

Option 3: Wrap using Exceptional SAM

Page 139: Jumping-with-java8

Make all Failure Explicit - Try<T>

View it as a singleton collection containing result of execution or failure.

Translated from Scala to Java - http://dhavaldalal.github.io/Java8-Try

Checked or Unchecked Exception.

Success

Failure

Input(s)

Value

Page 140: Jumping-with-java8

Boom!

Try<String> success = Try.with(() -> "Holiday");success.get(); // Holiday

//throws unchecked ArithmeticExceptionTry<Integer> failure = Try.with(() -> 2 / 0); failure.get(); //throws exception - failure does not return

with - Supplier, Consumer, Functions and Predicates.

Get the value back -> get()

Creating Try<T>

Page 141: Jumping-with-java8

Avoid get() - Failure throws Exception

Set Sensible Default

Integer value = Try.with(() -> Test.methodThatThrows()) .getOrElse(2);

System.out.println(value); // 2

class Test { static String methodThatThrows() throws Exception { throw new Exception("I never work"); }}

//throws checked ExceptionTry<Integer> failure = Try.with(() -> Test.methodThatThrows()); failure.get(); //throws exception - failure does not return

Instead, use getOrElse()

Boom!

Page 142: Jumping-with-java8

Try this or try that

Try<Boolean> authenticated = Try.with(() -> login(name, password)) .orElse(Try.with(() -> gmail(id, pwd)) .orElse(Try.with(() -> fbLogin(fbUser, fbPwd)) .orElse(Try.with(() -> false);

Or even that - Chain of Responsibility

Try<Boolean> authenticated = Try.with(() -> login(name, password)) .orElse(Try.with(() -> gmail(id, password));

Page 143: Jumping-with-java8

Doing side-effectsAvoid imperative check for presence of value.

Instead use forEach()

Try<String> holiday = Try.with(() -> "Diwali");

if (holiday.isSuccess()) { System.out.println(holiday.get()); // Diwali}

Try<String> holiday = Try.with(() -> "Diwali");holiday.forEach(System.out::println); // Diwali

Try<Integer> failure = Try.with(() -> 2 / 0); failure.forEach(System.out::println); // Prints Nothing

Page 144: Jumping-with-java8

Try Operations

Transforming - map

Filtering

Try<String> success = Try.with(() -> "Diwali");success.map(String::length) .forEach(System.out::println); // 6

String nothing = null;Try<Integer> failure = Try.with(() -> nothing.length());failure.map(length -> length * 2) .forEach(System.out::println); // Prints Nothing

Try<String> success = Try.with(() -> "Diwali");success.filter(s -> s.length() >= 6) .forEach(System.out::println); // Diwali

success.filter(s -> s.length() < 6) .forEach(System.out::println); // Prints Nothing

Page 145: Jumping-with-java8

flatMapping a TryFunctionThrowsException<String, Connection, SQLException> getConnection = DriverManager::getConnection;String url = "jdbc:oracle:oci8:scott/tiger@myhost";//Try getting a connection firstTry<Connection> connection = Try.with(getConnection, url);

Page 146: Jumping-with-java8

flatMapping a TryFunctionThrowsException<String, Connection, SQLException> getConnection = DriverManager::getConnection;String url = "jdbc:oracle:oci8:scott/tiger@myhost";//Try getting a connection firstTry<Connection> connection = Try.with(getConnection, url);

FunctionThrowsException<Connection, Statement, SQLException> createStatement = c -> c.createStatement();//Try creating a connection from statementTry<Try<Statement>> statement = connection.map(c -> Try.with(createStatement, c));

Page 147: Jumping-with-java8

flatMapping a TryFunctionThrowsException<String, Connection, SQLException> getConnection = DriverManager::getConnection;String url = "jdbc:oracle:oci8:scott/tiger@myhost";//Try getting a connection firstTry<Connection> connection = Try.with(getConnection, url);

FunctionThrowsException<Connection, Statement, SQLException> createStatement = c -> c.createStatement();//Try creating a connection from statementTry<Try<Statement>> statement = connection.map(c -> Try.with(createStatement, c));

BiFunctionThrowsException<Statement, String, ResultSet, SQLException> execute = (stmt, query) -> { stmt.execute(query); return stmt.getResultSet(); };String sql = "select * from events limit 1";//Try creating a result set from statementTry<Try<Try<ResultSet>>> resultSet = statement.map(c -> c.map(s -> Try.with(execute, s, sql)));

Page 148: Jumping-with-java8

FunctionThrowsException<ResultSet, Event, SQLException> toEvent = r -> { String type = r.getString(1); return new Event(type); }; //Try creating an event from result setTry<Try<Try<Try<Event>>>> event = resultSet.map(c -> c.map(s -> s.map(r -> Try.with(toEvent, r))));

//====== Summarizing what we did ======== Try<Try<Try<Try<Event>>>> nestedEvent = Try.with(getConnection, url) .map(c -> Try.with(createStatement, c)) .map(c -> c.map(s -> Try.with(execute, s, sql))) .map(c -> c.map(s -> s.map(r -> Try.with(toEvent, r))));

flatMapping a Try

Page 149: Jumping-with-java8

Try<Try<Try<Try<Event>>>> nestedEvent = Try.with(getConnection, url) .map(c -> Try.with(createStatement, c)) .map(c -> c.map(s -> Try.with(execute, s, sql))) .map(c -> c.map(s -> s.map(r -> Try.with(toEvent, r))));Look at

that nest of maps to get to event.

Connection Statement ResultSet Event

Try<Try<Try<Try<Event>>>>

Actual Event

This pattern is very common in FP when chaining map operations like this - its called Monad.

To reduce ‘map’ noise, use flatMap - it flattens and then maps.

At Each increasing level the actual code is pushed

inside by one level of indentation

Page 150: Jumping-with-java8

//flatMapping on Connection, Statement and ResultSetTry<Event> flattenedEvent = Try.with(getConnection, url) .flatMap(c -> Try.with(createStatement, c)) .flatMap(s -> Try.with(execute, s, sql)) .flatMap(r -> Try.with(toEvent, r));

flatMapping a TryThe nest is

now flattened

flatMap unpacks the result of Try and maps to another Try.

flatMap is the adobe for programmers - Erik Meijer

ConnectionTry StatementTry ResultSetTry EventTry

Page 151: Jumping-with-java8

Error HandlingSo far we focussed on happy path.

Question:

How can we react to errors in a functional style, especially now that we can chain the Trys?

Answer:

Lets look at chain of responsibility in error handling.

Page 152: Jumping-with-java8

The Happy PathTry.with(() -> 2 / 2) .recover((Throwable t) -> Double.NaN) .forEach(System.out::println); // 1.0

1

Try.with ( … )

recover ( … )

1

Page 153: Jumping-with-java8

Try.with(() -> 2 / 0) // throws ArithmeticException .recover((Throwable t) -> Double.NaN) .forEach(System.out::println); // NaN

Failure Recovery

NaN

Try.with ( … )

recover ( … )

Page 154: Jumping-with-java8

Failure PropagationTry.with(() -> Integer.valueOf(null)) .recover(t -> { if (t.getClass() == ArithmeticException.class) { return Double.NaN; } throw new RuntimeException(t); // Re-throw t }) .forEach(System.out::println);

Try.with ( … )

recover ( … )

Page 155: Jumping-with-java8

Chain of RecoveryTry<URL> nextURL = Try.with(() -> login(name, password)) .recover(ex -> { if (ex.getClass() == PasswordMismatchException.class) return forgotURL; // default throw new RuntimeException(ex); // Re-throw }) // 2 factor authentication returns dashboardURL .flatMap(user -> Try.with(() -> twoFactorAuth(user, _2FPasswd)) .recover(t -> loginURL);

Try.with ( … )

recover ( … )

recover ( … )

flatMap ( … )

URL

Unhandled Exceptions

Page 156: Jumping-with-java8

RecoverWith flatMap like

Try<URL> startURL = Try.with(() -> login(name, password)) // google login returns user .recoverWith(ex -> Try.with(() -> googleLogin(gmail)); // 2 factor authentication returns dashboardURL .flatMap(user -> Try.with(() -> twoFactorAuth(user, _2FPasswd)) .recover(t -> loginURL);

Try.with ( … )

recoverWith( … )

recover ( … )

flatMap ( … )

URL

Unhandled Exceptions

Page 157: Jumping-with-java8

class EventRepository { private final Map<Integer, Event> events = new HashMap<>();

public EventRepository() { … }

public Optional<Event> findById(final Integer id) { Objects.requireNonNull(id); // throws NPE return Optional.ofNullable(events.getOrDefault(id, null)); }}

EventRepository repository = new EventRepository();// Making NPE Explicit using TryTry<Optional<Date>> occurred = Try.with(() -> repository.findById(1).map(Event::occurredOn));

Date eventDate = occurred.toOptional() // Optional<Optional<Date>> .flatMap(x -> x) // Optional<Date> .orElse(null); // Date

System.out.println(eventDate);

Try -> Optional

Page 158: Jumping-with-java8

Make Latency Explicit CompletableFuture<T>Latency could be because of a CPU or an I/O intensive computation.

View CompletableFuture<T> as a singleton collection containing result of latent computation.

It is Asynchronous.Success

Failure

Input(s)

Value

Caller does not wait for future to complete as Future is non-blocking. Caller immediately

returns and continues execution on its thread.

Future runs in its own thread and calls back with result when the

latent task is complete.

Page 159: Jumping-with-java8

Creating a FutureCompletableFuture.supplyAsync

Integer expensiveSquare(Integer n) { try { Thread.sleep(1000); return n * n; } catch (InterruptedException ie) { }}

public static void main(String[] args) { CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> expensiveSquare(2.0)); System.out.println("Waiting for future to complete..."); try { Integer result = future.get(); //wait for it to complete System.out.println("Result = " + result); } catch (InterruptedException e) { } catch (ExecutionException e) { }}

Page 160: Jumping-with-java8

Creating a FutureCompletableFuture<Integer> square(Integer x, boolean canSucceed) { CompletableFuture<Integer> future = new CompletableFuture<>(); future.runAsync(() -> { System.out.println("Running future..."); try { Thread.sleep(1000); } catch (InterruptedException e) { } if (canSucceed) future.complete(x * x); else future.completeExceptionally(new RuntimeException("FAIL")); }); System.out.println("Returning future..."); return future;}

public static void main(String[] args) { CompletableFuture<Integer> future = square(2, true); System.out.println("Waiting for future to complete..."); try { Integer result = future.get(); //wait for it to complete System.out.println("Result = " + result); } catch (InterruptedException e) { } catch (ExecutionException e) { }}

Using new

Page 161: Jumping-with-java8

Getting Resultget() blocks until result is available.

For future completing with exception, get() throws

CompletableFuture<Integer> future = square(2, false);System.out.println("Waiting for future to complete...");try { Integer result = future.get(); System.out.println("Result = " + result);} catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }// Returning future...// Running future...// java.util.concurrent.ExecutionException:java.lang.RuntimeException:FAIL

Boom!

Page 162: Jumping-with-java8

Doing Side-effectsAvoid get(), instead use thenAccept()/thenRun() or thenAcceptAsync()/thenRunAsync()

Async methods run in a different thread from the thread pool

Non-async methods in the same thread on which the future completed.CompletableFuture.supplyAsync(() -> expensiveSquare(2)) .thenAccept(System.out::println); // 4

CompletableFuture.supplyAsync(() -> expensiveSquare(2)) .thenAcceptAsync(System.out::println); // 4

Page 163: Jumping-with-java8

Mapping Future

CompletableFuture.supplyAsync(() -> expensiveSquare(2)) .thenApply(result -> 2 * result) .thenAccept(System.out::println); // 8

CompletableFuture.supplyAsync(() -> expensiveSquare(2)) .thenApplyAsync(result -> 2 * result)) .thenAcceptAsync(System.out::println); // 8

Using thenApply() or thenApplyAsync()

Page 164: Jumping-with-java8

CompletableFuture.supplyAsync(() -> calculateNAV()) .thenCompose(nav -> persistToDB(nav)) .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> calculateNAV()) .thenComposeAsync(nav -> persistToDB(nav)) .thenAcceptAsync(System.out::println);

flatMapping FutureUsing thenCompose() or thenComposeAsync()

Page 165: Jumping-with-java8

Error handling and Recovery

So far we focussed on Happy path.

A future may not give what you expected, instead turn exceptional.

Recover using exceptionally()CompletableFuture<Integer> future = square(2, false);future.exceptionally(ex -> -1) .thenAccept(System.out::println);

// Returning future...// Running future...// -1

Page 166: Jumping-with-java8

CompletableFuture<Integer> future = square(2, false);future.handle((result, ex) -> { if (result != null) return result; else return -1; }) .thenAccept(System.out::println);

// Returning future...// Running future...// -1 when future fails or 4 when it succeeds.

Success and Recovery using handle().

Error handling and Recovery

Page 167: Jumping-with-java8

Combining Results from couple of Futures (and)

CompletableFuture<Integer> square(Integer x) { return CompletableFuture.completedFuture(x * x);}

CompletableFuture<Integer> cube(Integer x) { try { Thread.sleep(1000); } catch (InterruptedException e) { } return CompletableFuture.completedFuture(x * x * x);}

//Sum of square and cubesquare(2) .thenCombine(cube(3), (squared, cubed) -> squared + cubed) .thenAccept(System.out::println); // 31

square(2) .thenAcceptBoth(cube(3), (sqr, cub) -> System.out.println(sqr + cub)); // 31

Page 168: Jumping-with-java8

Accepting Either Future Results (or)CompletableFuture<Integer> slowPredictableSquare(Integer x) { CompletableFuture<Integer> future = new CompletableFuture<>(); future.runAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println("slowButPredictableSquare"); future.complete(x * x); }); return future;}CompletableFuture<Integer> fastUnpredictableSquare(Integer x) { boolean canRespond = random.nextBoolean(); CompletableFuture<Integer> future = new CompletableFuture<>(); future.runAsync(() -> { if (canRespond) { System.out.println("fastButUnpredictableSquare"); future.complete(x * x); } else { System.out.println("fastButUnpredictableSquare without answer"); } }); return future;}

Page 169: Jumping-with-java8

Accepting Either Future Results (or)

// If fastUnpredictableSquare completes first with an answer,// then it will print. In an event fastUnpredictableSquare does// not return, then slowPredictableSquare will give the answer. slowPredictableSquare(2) .acceptEither(fastUnpredictableSquare(2), System.out::println);

// fastUnpredictableSquare// 4

// Mapping whichever completes first. slowPredictableSquare(2) .applyToEither(fastUnpredictableSquare(2), result -> 2 * result) .thenAccept(System.out::println);

// fastUnpredictableSquare without answer// slowPredictableSquare // 8

Page 170: Jumping-with-java8

Combining Many Futures

CompletableFuture<Integer> [] futures = new CompletableFuture [] { slowPredictableSquare(1), slowPredictableSquare(2), slowPredictableSquare(3), slowPredictableSquare(4), slowPredictableSquare(5), slowPredictableSquare(6)};

CompletableFuture.allOf(futures);

Using allOf().

Ensures all the futures are completed, returns a Void future.

Page 171: Jumping-with-java8

Any of the Many FuturesUsing anyOf().

Ensures any one of the futures is completed, returns result of that completed future.

CompletableFuture<Integer> [] futures = new CompletableFuture [] { slowPredictableSquare(1), slowPredictableSquare(2), slowPredictableSquare(3), slowPredictableSquare(4), slowPredictableSquare(5), slowPredictableSquare(6)};

CompletableFuture.anyOf(futures) .thenAccept(System.out::println);

Page 172: Jumping-with-java8

Essence

A functional program empowers the developer with:

Reasoning with ease.

Testability.

Refactoring safely.

Composability.

Page 173: Jumping-with-java8

EssenceImmutability and purity makes laziness, referential transparency possible.

All effects that are not apparent in the return type of a method are abstracted and made explicit using a type.

For example

Dealing with null values is made explicit in Optional<T>

Exceptions as a effect is made explicit in Try<T>

Latency as a effect is made explicit in CompletableFuture<T>

Page 174: Jumping-with-java8

Referenceshttp://codejugalbandi.github.io/codejugalbandi.org

Ryan Lemmer, Dhaval Dalal

Functional Programming in Java

Venkat Subramaniam

Neophyte’s Guide to Scala

Daniel Westheide

Principles of Reactive Prog. Coursera Course

Martin Odersky, Erik Meijer and Roland Kuhn

http://en.wikipedia.org/wiki/Concatenative_programming_languagehttp://www.nurkiewicz.com/2013/05/java-8-definitive-guide-to.html

Page 175: Jumping-with-java8

Thank-You!