SlideShare a Scribd company logo
1 of 175
Download to read offline
Jumping With Java8
Come, Lambda Along!
dhaval.dalal@software-artisan.com
@softwareartisan
A Pure Function
Uses 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;
}
}
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 Function
Modifies or interacts with things
outside of its scope, and may also
return a value.
Outside of
its scope
Side-effecting Function
Changes 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.
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;
}
}
A Mother-like Function
Gives unconditionally without asking
for anything.
Example - the getters.
class Calculator {
private Integer memory;
public Integer recallMemory() {
return memory;
}
}
public String concat(String x, String y) {
return x + y;
}
String a = "referential";
String b = " transparency ";
String r1 = concat(a, b); // referential transparency
String r2 = concat(a, b); // referential transparency
Understanding
Referential
Transparency
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 transparency
String r2 = concat(a, b); // referential transparency referential
Understanding
Referential
Transparency
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
c.add(2, 3); // 5
c.add(2, 4); // 6
c.add(2, 3); // 5
c.add(2, 4); // 6
Referential
Transparency
Calculator 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); // 2
c.memoryPlus(3); // 5
c.memoryPlus(2); // 7
c.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.
How can we make
memoryPlus
Referential
Transparent?
Ref. Transparent
memoryPlus
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.
Reflections
Referential 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.
Reflections
To be referentially transparent,
function will require to work with
immutable data.
To be referentially transparent, a
function will require to be pure.
Why use Immutability
and Pure Functions?
Immutablity.
Promotes caching of objects - Flyweights.
Enables concurrent operations.
Pure Functions.
Promote Memoization - caching results of
expensive computations.
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.
Chai Chat: OO & FP
Chai Chat: OO & FP
We encapsulate data because we think
it will protect us from inadvertent
changes and build trust.
Chai Chat: OO & FP
We 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
Chai Chat: OO & FP
We 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.
Chai Chat: OO & FP
We 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
Chai Chat: OO & FP
Knowing the innards of an object,
causes coupling to parts, which comes
in the way of refactoring as
requirements change.
Chai Chat: OO & FP
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
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
Chai Chat: OO & FP
Functions that operate on immutable
data are then pure functions. For any
transformation they produce new data,
leaving the old unmodified.
f
Chai Chat: OO & FP
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
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
Encapsulation Vs Open Immutable Data
(structures).
Immutability Vs State-Time Conflation.
Don’t we value both?
Immutability
Encapsulation
Reflections
Good Deeds Happen Anonymously
An anonymous function - Lambda
Hard to name things
public 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
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
Lambda
Compiled 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
Functional Interfaces
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
…
}
Function<Float, Float> twice = x -> 2 * x;
twice.apply(3); // 6
// A function returns its argument unchanged
Function<Float, Float> identity = x -> x
@FunctionalInterface
public 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
Existing
Functional 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;
A Black-Hole like
Functional Interface
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
…
}
@FunctionalInterface
public 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);
Existing Consumers
public interface ActionListener extends EventListener {
public void actionPerformed(ActionEvent e);
}
// Can be written as
event -> System.out.println(event);
A Mother-like
Functional Interface
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Things Emerge
without
asking for anything
Supplier<String> mother = () -> "Love";
mother.get(); // Love
Existing Suppliers
public interface Callable<V> {
public V call();
}
// Can be written as:
() -> 2;
Mr.Spock-like
Functional Interface
@FunctionalInterface
public 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); // true
isEven.test(3); // false
Functional
Interface
Unimplemented
Method
Static Method
Default Method
1
0..*
0..*
Default & Static Methods
Default 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.
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.
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! Compilation
Boom!
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…
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?
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.
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!
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 function
Subsumed Factory Method.
Another Example
class 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();
}
}
Another Example
class 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
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
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;
}
}
//main
Transactions transactions = new Transactions(Arrays.asList(…));
ApprovalStrategy approvalStrategy = ApprovalStrategy.valueOf(transactions);
transactions.approve(approvalStrategy);
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;
}
}
//main
Transactions 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?
Subsumed Strategy
class 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;
}
}
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;
}
}
//main
Transactions 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
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
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");
}
Subsumed Proxy
Memoization
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);
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;
}
}
Subsumed Decorator
Function<Integer, Integer> expensiveSquare = x -> {
System.out.println("Now Squaring...");
try { Thread.sleep(2 * 1000); }
catch (Exception e) { }
return x * x;
};
// Decorate with time and memoize
Function<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.
Canthisbe
improved?
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
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>
});
Loan My Resource
Acquire
Loan resource for use
Release
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));
}
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
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
Refactored Code
List<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;
}
Refactored Code
String 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
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 Readability
What about memory
utilisation and
performance?
Enter Streams
Collection 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();
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.
Stream Operations
Iterate 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
Stream Ops
Combine adjacent elements
reduce, min, max, count, sum
Sort and Unique
sorted and distinct
take and drop
limit, skip
Parallel Stream
Double 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 ms
timer(numbers.parallelStream()); // Time: 2013 ms
Parallel Stream and
Side-Effects
String sentence = "all mimsy were the borogoves and the mome raths";
// Prints in the order the stream encountered
sentence.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 encountered
sentence.chars()
.parallel()
.forEachOrdered(ch -> System.out.println((char) ch));
Compilation Boom!!
“local variables referenced
from a lambda expression
must be final or effectively
final”
.forEach(word -> capitalized += word);
Stateful
Effectively 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);
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
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
Stateful
+
Parallel
==
Problem!
Stream Ops
Mutable 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…
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
Stateless
+
Parallel
==
No Problem!
Eager Evaluation
Java 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
Simulate Lazy Evaluation
Lazy - 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.
Generating Streams
Stream<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 generate
Stream<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
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.
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.
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.
Virtues of Laziness
With 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.
Finite from Infinite
List<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.
Given two lists:
[‘a’, ‘b’] and [1, 2]
Generate the combinations given below:
[[‘a’, 1], [‘a’, 2], [‘b’, 1], [‘b’, 2]]
Imperative Solution
List<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]]
Subsumed Nested Loops
List<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]]
Using Spliterator
Spliterators, 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
Streamable ResultSet
class 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); }

}

}
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;
}
}
}
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.
});
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.
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
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
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
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.
Reflections
Function 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
Reflections
Composition 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.
Outside
World
Side
Effecting
Functions
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
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.
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
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.
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 as
x -> y -> (z -> x + y + z);
// is same as
x -> (y -> (z -> x + y + z));
Currying
Function 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.
But, how does that
still make
programming spicier?
Let’s say we have a Customer repository
class 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());
}
}
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?
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);
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!
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!
R
eject
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
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
A
ccept
Solution 3
Making 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
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 3
Making our own curry
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 3
Making our own curry
A
ccept
Observations
We 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.
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
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).
Regular DI
interface 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);
}
}
//main
ApprovalStrategy singleMakerChecker = new SingleMakerChecker();
Clearing clearing = new Clearing(singleMakerChecker);
clearing.approve(ts);
Curried DI
interface Transaction { }
interface ApprovalStrategy {
boolean approve(List<Transaction> ts);
//…
}
class Clearing {
public
Function<ApprovalStrategy, Boolean> approve(List<Transaction> ts) {
return aps -> aps.approve(ts);
}
}
//main
Clearing 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());
Reflections
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
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
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()
Boom!
Set Sensible Default
Avoid get() - it throws Exception for
absence of value
Instead use orElse, orElseGet, orElseThrow
Event shopping = new Event(null);
System.out.println(shopping.type()
.orElse("Other")); // Other
System.out.println(shopping.type()
.orElseGet(() -> "Other"));// Other
shopping.type().orElseThrow(() ->
new IllegalArgumentException("Empty or null")));
Event shopping = new Event(null);
System.out.println(shopping.type().get());
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
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
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
Checked Exceptions
+
Lambda
==
Pain!
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.
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.
Option 3: Wrap using
Exceptional SAM
http://mail.openjdk.java.net/pipermail/lambda-dev/2013-January/007662.html
@FunctionalInterface
interface 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); }
};
}
}
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
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
Boom!
Try<String> success = Try.with(() -> "Holiday");
success.get(); // Holiday
//throws unchecked ArithmeticException
Try<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>
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 Exception
Try<Integer> failure = Try.with(() -> Test.methodThatThrows());
failure.get(); //throws exception - failure does not return
Instead, use getOrElse()
Boom!
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));
Doing side-effects
Avoid 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
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
flatMapping a Try
FunctionThrowsException<String, Connection, SQLException>
getConnection = DriverManager::getConnection;
String url = "jdbc:oracle:oci8:scott/tiger@myhost";
//Try getting a connection first
Try<Connection> connection = Try.with(getConnection, url);
flatMapping a Try
FunctionThrowsException<String, Connection, SQLException>
getConnection = DriverManager::getConnection;
String url = "jdbc:oracle:oci8:scott/tiger@myhost";
//Try getting a connection first
Try<Connection> connection = Try.with(getConnection, url);
FunctionThrowsException<Connection, Statement, SQLException>
createStatement = c -> c.createStatement();
//Try creating a connection from statement
Try<Try<Statement>> statement =
connection.map(c -> Try.with(createStatement, c));
flatMapping a Try
FunctionThrowsException<String, Connection, SQLException>
getConnection = DriverManager::getConnection;
String url = "jdbc:oracle:oci8:scott/tiger@myhost";
//Try getting a connection first
Try<Connection> connection = Try.with(getConnection, url);
FunctionThrowsException<Connection, Statement, SQLException>
createStatement = c -> c.createStatement();
//Try creating a connection from statement
Try<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 statement
Try<Try<Try<ResultSet>>> resultSet =
statement.map(c -> c.map(s -> Try.with(execute, s, sql)));
FunctionThrowsException<ResultSet, Event, SQLException> toEvent =
r -> {
String type = r.getString(1);
return new Event(type);
};
//Try creating an event from result set
Try<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
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
//flatMapping on Connection, Statement and ResultSet
Try<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 Try
The 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
Error Handling
So 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.
The Happy Path
Try.with(() -> 2 / 2)
.recover((Throwable t) -> Double.NaN)
.forEach(System.out::println); // 1.0
1
Try.with ( … )
recover ( … )
1
Try.with(() -> 2 / 0) // throws ArithmeticException
.recover((Throwable t) -> Double.NaN)
.forEach(System.out::println); // NaN
Failure Recovery
NaN
Try.with ( … )
recover ( … )
Failure Propagation
Try.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 ( … )
Chain of Recovery
Try<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
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
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 Try
Try<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
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.
Creating a Future
CompletableFuture.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) { }
}
Creating a Future
CompletableFuture<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
Getting Result
get() 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!
Doing Side-effects
Avoid 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
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()
CompletableFuture.supplyAsync(() -> calculateNAV())
.thenCompose(nav -> persistToDB(nav))
.thenAccept(System.out::println);
CompletableFuture.supplyAsync(() -> calculateNAV())
.thenComposeAsync(nav -> persistToDB(nav))
.thenAcceptAsync(System.out::println);
flatMapping Future
Using thenCompose() or thenComposeAsync()
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
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
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 cube
square(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
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;
}
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
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.
Any of the Many Futures
Using 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);
Essence
A functional program empowers the
developer with:
Reasoning with ease.
Testability.
Refactoring safely.
Composability.
Essence
Immutability 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>
References
http://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_language
http://www.nurkiewicz.com/2013/05/java-8-definitive-
guide-to.html
Thank-You!

More Related Content

What's hot

Action Jackson! Effective JSON processing in Spring Boot Applications
Action Jackson! Effective JSON processing in Spring Boot ApplicationsAction Jackson! Effective JSON processing in Spring Boot Applications
Action Jackson! Effective JSON processing in Spring Boot ApplicationsJoris Kuipers
 
Java 8 Default Methods
Java 8 Default MethodsJava 8 Default Methods
Java 8 Default MethodsHaim Michael
 
Mars rover-extension
Mars rover-extensionMars rover-extension
Mars rover-extensionDhaval Dalal
 
Java 10 New Features
Java 10 New FeaturesJava 10 New Features
Java 10 New FeaturesAli BAKAN
 
Testing with JUnit 5 and Spring
Testing with JUnit 5 and SpringTesting with JUnit 5 and Spring
Testing with JUnit 5 and SpringVMware Tanzu
 
Top C Language Interview Questions and Answer
Top C Language Interview Questions and AnswerTop C Language Interview Questions and Answer
Top C Language Interview Questions and AnswerVineet Kumar Saini
 
C, C++ Interview Questions Part - 1
C, C++ Interview Questions Part - 1C, C++ Interview Questions Part - 1
C, C++ Interview Questions Part - 1ReKruiTIn.com
 
java 8 new features
java 8 new features java 8 new features
java 8 new features Rohit Verma
 
Decision making and branching in c programming
Decision making and branching in c programmingDecision making and branching in c programming
Decision making and branching in c programmingPriyansh Thakar
 
clean code book summary - uncle bob - English version
clean code book summary - uncle bob - English versionclean code book summary - uncle bob - English version
clean code book summary - uncle bob - English versionsaber tabatabaee
 
Java Basics
Java BasicsJava Basics
Java BasicsSunil OS
 
OCA Java SE 8 Exam Chapter 3 Core Java APIs
OCA Java SE 8 Exam Chapter 3 Core Java APIsOCA Java SE 8 Exam Chapter 3 Core Java APIs
OCA Java SE 8 Exam Chapter 3 Core Java APIsİbrahim Kürce
 

What's hot (20)

Clean code slide
Clean code slideClean code slide
Clean code slide
 
Action Jackson! Effective JSON processing in Spring Boot Applications
Action Jackson! Effective JSON processing in Spring Boot ApplicationsAction Jackson! Effective JSON processing in Spring Boot Applications
Action Jackson! Effective JSON processing in Spring Boot Applications
 
Java Strings
Java StringsJava Strings
Java Strings
 
Java 8 Default Methods
Java 8 Default MethodsJava 8 Default Methods
Java 8 Default Methods
 
Loops in C
Loops in CLoops in C
Loops in C
 
Mars rover-extension
Mars rover-extensionMars rover-extension
Mars rover-extension
 
OOP and FP
OOP and FPOOP and FP
OOP and FP
 
Java 10 New Features
Java 10 New FeaturesJava 10 New Features
Java 10 New Features
 
Testing with JUnit 5 and Spring
Testing with JUnit 5 and SpringTesting with JUnit 5 and Spring
Testing with JUnit 5 and Spring
 
Optional in Java 8
Optional in Java 8Optional in Java 8
Optional in Java 8
 
Java interface
Java interfaceJava interface
Java interface
 
Top C Language Interview Questions and Answer
Top C Language Interview Questions and AnswerTop C Language Interview Questions and Answer
Top C Language Interview Questions and Answer
 
STORAGE CLASSES
STORAGE CLASSESSTORAGE CLASSES
STORAGE CLASSES
 
C, C++ Interview Questions Part - 1
C, C++ Interview Questions Part - 1C, C++ Interview Questions Part - 1
C, C++ Interview Questions Part - 1
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
java 8 new features
java 8 new features java 8 new features
java 8 new features
 
Decision making and branching in c programming
Decision making and branching in c programmingDecision making and branching in c programming
Decision making and branching in c programming
 
clean code book summary - uncle bob - English version
clean code book summary - uncle bob - English versionclean code book summary - uncle bob - English version
clean code book summary - uncle bob - English version
 
Java Basics
Java BasicsJava Basics
Java Basics
 
OCA Java SE 8 Exam Chapter 3 Core Java APIs
OCA Java SE 8 Exam Chapter 3 Core Java APIsOCA Java SE 8 Exam Chapter 3 Core Java APIs
OCA Java SE 8 Exam Chapter 3 Core Java APIs
 

Similar to Jumping-with-java8

Static Keyword Static is a keyword in C++ used to give special chara.pdf
  Static Keyword Static is a keyword in C++ used to give special chara.pdf  Static Keyword Static is a keyword in C++ used to give special chara.pdf
Static Keyword Static is a keyword in C++ used to give special chara.pdfKUNALHARCHANDANI1
 
New microsoft office word document (2)
New microsoft office word document (2)New microsoft office word document (2)
New microsoft office word document (2)rashmita_mishra
 
Functional Swift
Functional SwiftFunctional Swift
Functional SwiftGeison Goes
 
Incredible Machine with Pipelines and Generators
Incredible Machine with Pipelines and GeneratorsIncredible Machine with Pipelines and Generators
Incredible Machine with Pipelines and Generatorsdantleech
 
Practical Meta Programming
Practical Meta ProgrammingPractical Meta Programming
Practical Meta ProgrammingReggie Meisler
 
Generalized Functors - Realizing Command Design Pattern in C++
Generalized Functors - Realizing Command Design Pattern in C++Generalized Functors - Realizing Command Design Pattern in C++
Generalized Functors - Realizing Command Design Pattern in C++ppd1961
 
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Ovidiu Farauanu
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongMario Fusco
 
solidity programming solidity programming
solidity programming solidity programmingsolidity programming solidity programming
solidity programming solidity programmingMohan Kumar Ch
 
C questions
C questionsC questions
C questionsparm112
 
C++ Interview Question And Answer
C++ Interview Question And AnswerC++ Interview Question And Answer
C++ Interview Question And AnswerJagan Mohan Bishoyi
 
C++ questions And Answer
C++ questions And AnswerC++ questions And Answer
C++ questions And Answerlavparmar007
 
1183 c-interview-questions-and-answers
1183 c-interview-questions-and-answers1183 c-interview-questions-and-answers
1183 c-interview-questions-and-answersAkash Gawali
 

Similar to Jumping-with-java8 (20)

Functional Go
Functional GoFunctional Go
Functional Go
 
Static Keyword Static is a keyword in C++ used to give special chara.pdf
  Static Keyword Static is a keyword in C++ used to give special chara.pdf  Static Keyword Static is a keyword in C++ used to give special chara.pdf
Static Keyword Static is a keyword in C++ used to give special chara.pdf
 
New microsoft office word document (2)
New microsoft office word document (2)New microsoft office word document (2)
New microsoft office word document (2)
 
Functional programming
Functional programmingFunctional programming
Functional programming
 
Functional Swift
Functional SwiftFunctional Swift
Functional Swift
 
Incredible Machine with Pipelines and Generators
Incredible Machine with Pipelines and GeneratorsIncredible Machine with Pipelines and Generators
Incredible Machine with Pipelines and Generators
 
cp Module4(1)
cp Module4(1)cp Module4(1)
cp Module4(1)
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
Practical Meta Programming
Practical Meta ProgrammingPractical Meta Programming
Practical Meta Programming
 
Generalized Functors - Realizing Command Design Pattern in C++
Generalized Functors - Realizing Command Design Pattern in C++Generalized Functors - Realizing Command Design Pattern in C++
Generalized Functors - Realizing Command Design Pattern in C++
 
DS Unit 6.ppt
DS Unit 6.pptDS Unit 6.ppt
DS Unit 6.ppt
 
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
 
Scope of variables
Scope of variablesScope of variables
Scope of variables
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are Wrong
 
solidity programming solidity programming
solidity programming solidity programmingsolidity programming solidity programming
solidity programming solidity programming
 
C questions
C questionsC questions
C questions
 
Scope of variables
Scope of variablesScope of variables
Scope of variables
 
C++ Interview Question And Answer
C++ Interview Question And AnswerC++ Interview Question And Answer
C++ Interview Question And Answer
 
C++ questions And Answer
C++ questions And AnswerC++ questions And Answer
C++ questions And Answer
 
1183 c-interview-questions-and-answers
1183 c-interview-questions-and-answers1183 c-interview-questions-and-answers
1183 c-interview-questions-and-answers
 

More from Dhaval Dalal

Test Pyramid in Microservices Context
Test Pyramid in Microservices ContextTest Pyramid in Microservices Context
Test Pyramid in Microservices ContextDhaval Dalal
 
Booting into functional programming
Booting into functional programmingBooting into functional programming
Booting into functional programmingDhaval Dalal
 
Currying and Partial Function Application (PFA)
Currying and Partial Function Application (PFA)Currying and Partial Function Application (PFA)
Currying and Partial Function Application (PFA)Dhaval Dalal
 
Creating Lazy stream in CSharp
Creating Lazy stream in CSharpCreating Lazy stream in CSharp
Creating Lazy stream in CSharpDhaval Dalal
 
Json Viewer Stories
Json Viewer StoriesJson Viewer Stories
Json Viewer StoriesDhaval Dalal
 
How Is Homeopathy Near To Yoga?
How Is Homeopathy Near To Yoga?How Is Homeopathy Near To Yoga?
How Is Homeopathy Near To Yoga?Dhaval Dalal
 
Approaching ATDD/BDD
Approaching ATDD/BDDApproaching ATDD/BDD
Approaching ATDD/BDDDhaval Dalal
 
Paradigms Code jugalbandi
Paradigms Code jugalbandiParadigms Code jugalbandi
Paradigms Code jugalbandiDhaval Dalal
 
Data Reconciliation
Data ReconciliationData Reconciliation
Data ReconciliationDhaval Dalal
 
DRYing to Monad in Java8
DRYing to Monad in Java8DRYing to Monad in Java8
DRYing to Monad in Java8Dhaval Dalal
 
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr20154-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015Dhaval Dalal
 
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar20153-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015Dhaval Dalal
 
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015Dhaval Dalal
 
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-IssueCodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-IssueDhaval Dalal
 
The tao-of-transformation-workshop
The tao-of-transformation-workshopThe tao-of-transformation-workshop
The tao-of-transformation-workshopDhaval Dalal
 
Grooming with Groovy
Grooming with GroovyGrooming with Groovy
Grooming with GroovyDhaval Dalal
 
Language portfolio
Language portfolioLanguage portfolio
Language portfolioDhaval Dalal
 

More from Dhaval Dalal (20)

Test Pyramid in Microservices Context
Test Pyramid in Microservices ContextTest Pyramid in Microservices Context
Test Pyramid in Microservices Context
 
Code Retreat
Code RetreatCode Retreat
Code Retreat
 
Booting into functional programming
Booting into functional programmingBooting into functional programming
Booting into functional programming
 
Currying and Partial Function Application (PFA)
Currying and Partial Function Application (PFA)Currying and Partial Function Application (PFA)
Currying and Partial Function Application (PFA)
 
Creating Lazy stream in CSharp
Creating Lazy stream in CSharpCreating Lazy stream in CSharp
Creating Lazy stream in CSharp
 
Json Viewer Stories
Json Viewer StoriesJson Viewer Stories
Json Viewer Stories
 
Value Objects
Value ObjectsValue Objects
Value Objects
 
How Is Homeopathy Near To Yoga?
How Is Homeopathy Near To Yoga?How Is Homeopathy Near To Yoga?
How Is Homeopathy Near To Yoga?
 
Approaching ATDD/BDD
Approaching ATDD/BDDApproaching ATDD/BDD
Approaching ATDD/BDD
 
Paradigms Code jugalbandi
Paradigms Code jugalbandiParadigms Code jugalbandi
Paradigms Code jugalbandi
 
Data Reconciliation
Data ReconciliationData Reconciliation
Data Reconciliation
 
DRYing to Monad in Java8
DRYing to Monad in Java8DRYing to Monad in Java8
DRYing to Monad in Java8
 
CodeRetreat
CodeRetreatCodeRetreat
CodeRetreat
 
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr20154-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
4-Code-Jugalbandi-destructuring-patternmatching-healthycode#apr2015
 
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar20153-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
3-CodeJugalbandi-currying-pfa-healthycodemagazine#mar2015
 
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
CodeJugalbandi-Sequencing-HealthyCode-Magazine-Feb-2015
 
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-IssueCodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
CodeJugalbandi-Expression-Problem-HealthyCode-Magazine#Jan-2015-Issue
 
The tao-of-transformation-workshop
The tao-of-transformation-workshopThe tao-of-transformation-workshop
The tao-of-transformation-workshop
 
Grooming with Groovy
Grooming with GroovyGrooming with Groovy
Grooming with Groovy
 
Language portfolio
Language portfolioLanguage portfolio
Language portfolio
 

Recently uploaded

SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 

Recently uploaded (20)

SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 

Jumping-with-java8

  • 1. Jumping With Java8 Come, Lambda Along! dhaval.dalal@software-artisan.com @softwareartisan
  • 2. A Pure Function Uses 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; } }
  • 3. 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 Function Modifies or interacts with things outside of its scope, and may also return a value. Outside of its scope
  • 4. Side-effecting Function Changes 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.
  • 5. 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; } }
  • 6. A Mother-like Function Gives unconditionally without asking for anything. Example - the getters. class Calculator { private Integer memory; public Integer recallMemory() { return memory; } }
  • 7. public String concat(String x, String y) { return x + y; } String a = "referential"; String b = " transparency "; String r1 = concat(a, b); // referential transparency String r2 = concat(a, b); // referential transparency Understanding Referential Transparency
  • 8. 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 transparency String r2 = concat(a, b); // referential transparency referential Understanding Referential Transparency
  • 9. 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
  • 10. c.add(2, 3); // 5 c.add(2, 4); // 6 c.add(2, 3); // 5 c.add(2, 4); // 6 Referential Transparency Calculator 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); // 2 c.memoryPlus(3); // 5 c.memoryPlus(2); // 7 c.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.
  • 11. How can we make memoryPlus Referential Transparent?
  • 12. Ref. Transparent memoryPlus 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.
  • 13. Reflections Referential 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.
  • 14. Reflections To be referentially transparent, function will require to work with immutable data. To be referentially transparent, a function will require to be pure.
  • 15. Why use Immutability and Pure Functions? Immutablity. Promotes caching of objects - Flyweights. Enables concurrent operations. Pure Functions. Promote Memoization - caching results of expensive computations.
  • 16. 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.
  • 18. Chai Chat: OO & FP We encapsulate data because we think it will protect us from inadvertent changes and build trust.
  • 19. Chai Chat: OO & FP We 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
  • 20. Chai Chat: OO & FP We 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.
  • 21. Chai Chat: OO & FP We 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
  • 23. Knowing the innards of an object, causes coupling to parts, which comes in the way of refactoring as requirements change. Chai Chat: OO & FP
  • 24. 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
  • 25. 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
  • 27. Functions that operate on immutable data are then pure functions. For any transformation they produce new data, leaving the old unmodified. f Chai Chat: OO & FP
  • 28. 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
  • 29. 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
  • 30. Encapsulation Vs Open Immutable Data (structures). Immutability Vs State-Time Conflation. Don’t we value both? Immutability Encapsulation Reflections
  • 31. Good Deeds Happen Anonymously An anonymous function - Lambda Hard to name things public 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
  • 32. 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
  • 33. Lambda Compiled 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
  • 34. Functional Interfaces @FunctionalInterface public interface Function<T, R> { R apply(T t); … } Function<Float, Float> twice = x -> 2 * x; twice.apply(3); // 6 // A function returns its argument unchanged Function<Float, Float> identity = x -> x @FunctionalInterface public 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
  • 35. Existing Functional 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;
  • 36. A Black-Hole like Functional Interface @FunctionalInterface public interface Consumer<T> { void accept(T t); … } @FunctionalInterface public 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);
  • 37. Existing Consumers public interface ActionListener extends EventListener { public void actionPerformed(ActionEvent e); } // Can be written as event -> System.out.println(event);
  • 38. A Mother-like Functional Interface @FunctionalInterface public interface Supplier<T> { T get(); } Things Emerge without asking for anything Supplier<String> mother = () -> "Love"; mother.get(); // Love
  • 39. Existing Suppliers public interface Callable<V> { public V call(); } // Can be written as: () -> 2;
  • 40. Mr.Spock-like Functional Interface @FunctionalInterface public 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); // true isEven.test(3); // false
  • 42. Default & Static Methods Default 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.
  • 43. 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.
  • 44. 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! Compilation Boom!
  • 45. 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…
  • 46. 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?
  • 47. 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.
  • 48. 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!
  • 49. 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 function Subsumed Factory Method.
  • 50. Another Example class 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(); } }
  • 51. Another Example class 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
  • 52. 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
  • 53. 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; } } //main Transactions transactions = new Transactions(Arrays.asList(…)); ApprovalStrategy approvalStrategy = ApprovalStrategy.valueOf(transactions); transactions.approve(approvalStrategy);
  • 54. 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; } } //main Transactions 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?
  • 55. Subsumed Strategy class 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; } }
  • 56. 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; } } //main Transactions 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
  • 57. 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
  • 58. 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"); }
  • 59. Subsumed Proxy Memoization 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);
  • 60. 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; } }
  • 61. Subsumed Decorator Function<Integer, Integer> expensiveSquare = x -> { System.out.println("Now Squaring..."); try { Thread.sleep(2 * 1000); } catch (Exception e) { } return x * x; }; // Decorate with time and memoize Function<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.
  • 62. Canthisbe improved? 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
  • 63. 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> });
  • 64. Loan My Resource Acquire Loan resource for use Release
  • 65. 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)); }
  • 66. 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
  • 67. 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
  • 68. Refactored Code List<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; }
  • 69. Refactored Code String 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
  • 70. 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 Readability What about memory utilisation and performance?
  • 71. Enter Streams Collection 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();
  • 72. 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.
  • 73. Stream Operations Iterate 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
  • 74. Stream Ops Combine adjacent elements reduce, min, max, count, sum Sort and Unique sorted and distinct take and drop limit, skip
  • 75. Parallel Stream Double 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 ms timer(numbers.parallelStream()); // Time: 2013 ms
  • 76. Parallel Stream and Side-Effects String sentence = "all mimsy were the borogoves and the mome raths"; // Prints in the order the stream encountered sentence.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 encountered sentence.chars() .parallel() .forEachOrdered(ch -> System.out.println((char) ch));
  • 77. Compilation Boom!! “local variables referenced from a lambda expression must be final or effectively final” .forEach(word -> capitalized += word); Stateful Effectively 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);
  • 78. 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
  • 79. 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
  • 81. Stream Ops Mutable 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…
  • 82. 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
  • 84. Eager Evaluation Java 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
  • 85. Simulate Lazy Evaluation Lazy - 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.
  • 86. Generating Streams Stream<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 generate Stream<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
  • 87. 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.
  • 88. 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.
  • 89. 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.
  • 90. Virtues of Laziness With 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.
  • 91. Finite from Infinite List<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.
  • 92. Given two lists: [‘a’, ‘b’] and [1, 2] Generate the combinations given below: [[‘a’, 1], [‘a’, 2], [‘b’, 1], [‘b’, 2]] Imperative Solution List<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]]
  • 93. Subsumed Nested Loops List<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]]
  • 94. Using Spliterator Spliterators, 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
  • 95. Streamable ResultSet class 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); }
 }
 }
  • 96. 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; } } }
  • 97. 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. });
  • 98. 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.
  • 99. 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
  • 100. 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
  • 101. 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
  • 102. 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.
  • 103. Reflections Function 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
  • 104. Reflections Composition 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.
  • 105. Outside World Side Effecting Functions 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
  • 106. 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.
  • 107. 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
  • 108. 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.
  • 109. 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 as x -> y -> (z -> x + y + z); // is same as x -> (y -> (z -> x + y + z));
  • 110. Currying Function 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.
  • 111. But, how does that still make programming spicier?
  • 112. Let’s say we have a Customer repository class 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()); } }
  • 113. 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?
  • 114. 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);
  • 115. 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!
  • 116. 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! R eject
  • 117. 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
  • 118. 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 A ccept
  • 119. Solution 3 Making 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
  • 120. 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 3 Making our own curry
  • 121. 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 3 Making our own curry A ccept
  • 122. Observations We 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.
  • 123. 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
  • 124. 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).
  • 125. Regular DI interface 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); } } //main ApprovalStrategy singleMakerChecker = new SingleMakerChecker(); Clearing clearing = new Clearing(singleMakerChecker); clearing.approve(ts);
  • 126. Curried DI interface Transaction { } interface ApprovalStrategy { boolean approve(List<Transaction> ts); //… } class Clearing { public Function<ApprovalStrategy, Boolean> approve(List<Transaction> ts) { return aps -> aps.approve(ts); } } //main Clearing 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());
  • 127. Reflections 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
  • 128. 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
  • 129. 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()
  • 130. Boom! Set Sensible Default Avoid get() - it throws Exception for absence of value Instead use orElse, orElseGet, orElseThrow Event shopping = new Event(null); System.out.println(shopping.type() .orElse("Other")); // Other System.out.println(shopping.type() .orElseGet(() -> "Other"));// Other shopping.type().orElseThrow(() -> new IllegalArgumentException("Empty or null"))); Event shopping = new Event(null); System.out.println(shopping.type().get());
  • 131. 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
  • 132. 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
  • 133. 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
  • 135. 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.
  • 136. 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.
  • 137. Option 3: Wrap using Exceptional SAM http://mail.openjdk.java.net/pipermail/lambda-dev/2013-January/007662.html @FunctionalInterface interface 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); } }; } }
  • 138. 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
  • 139. 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
  • 140. Boom! Try<String> success = Try.with(() -> "Holiday"); success.get(); // Holiday //throws unchecked ArithmeticException Try<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>
  • 141. 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 Exception Try<Integer> failure = Try.with(() -> Test.methodThatThrows()); failure.get(); //throws exception - failure does not return Instead, use getOrElse() Boom!
  • 142. 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));
  • 143. Doing side-effects Avoid 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
  • 144. 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
  • 145. flatMapping a Try FunctionThrowsException<String, Connection, SQLException> getConnection = DriverManager::getConnection; String url = "jdbc:oracle:oci8:scott/tiger@myhost"; //Try getting a connection first Try<Connection> connection = Try.with(getConnection, url);
  • 146. flatMapping a Try FunctionThrowsException<String, Connection, SQLException> getConnection = DriverManager::getConnection; String url = "jdbc:oracle:oci8:scott/tiger@myhost"; //Try getting a connection first Try<Connection> connection = Try.with(getConnection, url); FunctionThrowsException<Connection, Statement, SQLException> createStatement = c -> c.createStatement(); //Try creating a connection from statement Try<Try<Statement>> statement = connection.map(c -> Try.with(createStatement, c));
  • 147. flatMapping a Try FunctionThrowsException<String, Connection, SQLException> getConnection = DriverManager::getConnection; String url = "jdbc:oracle:oci8:scott/tiger@myhost"; //Try getting a connection first Try<Connection> connection = Try.with(getConnection, url); FunctionThrowsException<Connection, Statement, SQLException> createStatement = c -> c.createStatement(); //Try creating a connection from statement Try<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 statement Try<Try<Try<ResultSet>>> resultSet = statement.map(c -> c.map(s -> Try.with(execute, s, sql)));
  • 148. FunctionThrowsException<ResultSet, Event, SQLException> toEvent = r -> { String type = r.getString(1); return new Event(type); }; //Try creating an event from result set Try<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
  • 149. 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
  • 150. //flatMapping on Connection, Statement and ResultSet Try<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 Try The 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
  • 151. Error Handling So 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.
  • 152. The Happy Path Try.with(() -> 2 / 2) .recover((Throwable t) -> Double.NaN) .forEach(System.out::println); // 1.0 1 Try.with ( … ) recover ( … ) 1
  • 153. Try.with(() -> 2 / 0) // throws ArithmeticException .recover((Throwable t) -> Double.NaN) .forEach(System.out::println); // NaN Failure Recovery NaN Try.with ( … ) recover ( … )
  • 154. Failure Propagation Try.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 ( … )
  • 155. Chain of Recovery Try<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
  • 156. 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
  • 157. 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 Try Try<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
  • 158. 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.
  • 159. Creating a Future CompletableFuture.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) { } }
  • 160. Creating a Future CompletableFuture<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
  • 161. Getting Result get() 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!
  • 162. Doing Side-effects Avoid 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
  • 163. 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()
  • 164. CompletableFuture.supplyAsync(() -> calculateNAV()) .thenCompose(nav -> persistToDB(nav)) .thenAccept(System.out::println); CompletableFuture.supplyAsync(() -> calculateNAV()) .thenComposeAsync(nav -> persistToDB(nav)) .thenAcceptAsync(System.out::println); flatMapping Future Using thenCompose() or thenComposeAsync()
  • 165. 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
  • 166. 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
  • 167. 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 cube square(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
  • 168. 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; }
  • 169. 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
  • 170. 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.
  • 171. Any of the Many Futures Using 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);
  • 172. Essence A functional program empowers the developer with: Reasoning with ease. Testability. Refactoring safely. Composability.
  • 173. Essence Immutability 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>
  • 174. References http://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_language http://www.nurkiewicz.com/2013/05/java-8-definitive- guide-to.html