SlideShare a Scribd company logo
λazy
by Mario Fusco
Red Hat – Principal Software Engineer
@mariofusco
Lazy Evaluation
Lazy evaluation (or call-by-name) is an evaluation
strategy which delays the evaluation of an expression
until its value is needed
I know what to do.
Wake me up when
you really need it
Strictness vs. Laziness
Strictness is a property of functions (or methods in Java).
A strict function always evaluates its arguments as soon
as they’re passed to it.
Conversely a lazy function may choose not to evaluate one
or more of its arguments and in general it will evaluate
them only when they’re actually needed.
To recap,
strictness is about doing things,
laziness is about noting things to do.
Java is a strict language ...
… with some notable (and unavoidable) exceptions
✔
Boolean operators || and &&
✔
Ternary operator ? :
✔
if ... else
✔
for/while loops
✔
Java 8 streams
Q: Can exist a totally strict language?
Java is a strict language ...
… with some notable (and unavoidable) exceptions
✔
Boolean operators || and &&
✔
Ternary operator ? :
✔
if ... else
✔
for/while loops
✔
Java 8 streams
Q: Can exist a totally strict language?
A: It’s hard if not impossible to imagine how it could work
boolean isAdult = person != null && person.getAge() >= 18;
Turning Java into a lazy language
<T> T ternary(boolean pred, T first, T second) {
if (pred) {
return first;
} else {
return second;
}
}
String val1() {
return "first";
}
String val2() {
return "second";
}
String result1 = bool ? val1() : val2();
String result2 = ternary(bool, val1(), val2());
Turning Java into a lazy language
<T> T ternary(boolean pred, Supplier<T> first, Supplier<T> second) {
if (pred) {
return first.get();
} else {
return second.get();
}
}
String val1() {
return "first";
}
String val2() {
return "second";
}
String result1 = bool ? val1() : val2();
String result2 = ternary(bool, () -> val1(), () -> val2());
A simple practical example: logging
// pre-Java 8 style optimization
if (logger.isTraceEnabled()) {
logger.trace("Some long-running operation returned {}",
expensiveOperation());
}
A simple practical example: logging
// pre-Java 8 style optimization
if (logger.isTraceEnabled()) {
logger.trace("Some long-running operation returned {}",
expensiveOperation());
}
// Java 8 style optimization using laziness
logger.trace("Some long-running operation returned {}",
() -> expensiveOperation());
* from Apache Log4J 2 docs
no need to explicitly check the log level:
the lambda expression is not evaluated
if the TRACE level is not enabled *
Laziness: the ultimate performance
optimization technique
Performance optimization pro tip:
before trying to inline/optimize/parallelize a piece of code,
ask yourself if you could avoid to run it at all.
Laziness is
probably the
only form of
performance
optimization
which is (almost)
never premature
There is nothing so useless as doing efficiently
something that should not be done at all
The case of Java 8 Streams
IntStream.iterate( 1, i -> i+1 )
.map( i -> i * 2 )
.filter( i -> i > 5 )
.findFirst();
The case of Java 8 Streams
IntStream.iterate( 1, i -> i+1 )
.map( i -> i * 2 )
.filter( i -> i > 5 )
.findFirst();
Thank to laziness the Stream
can be potentially infinite
The case of Java 8 Streams
IntStream.iterate( 1, i -> i+1 )
.map( i -> i * 2 )
.filter( i -> i > 5 )
.findFirst();
Thank to laziness the Stream
can be potentially infinite
Intermediate operations are lazy:
they don’t perform any action until
a terminal operation is reached
The case of Java 8 Streams
IntStream.iterate( 1, i -> i+1 )
.map( i -> i * 2 )
.filter( i -> i > 5 )
.findFirst();
Thank to laziness the Stream
can be potentially infinite
Intermediate operations are lazy:
they don’t perform any action until
a terminal operation is reached
Only the terminal operation
triggers the pipeline of
computations
A Stream is not a data structure.
It is the lazy specification of a how
to manipulate a set of data.
Things you can’t do without laziness
There are several algorithms that can’t be
(reasonably) implemented without laziness.
For example let’s consider the following:
1. Take the list of positive integers.
2. Filter the primes.
3. Return the list of the first ten results.
Wait! I can achieve the same with a
strict algorithm
Yes, but how?
1. Take the first integer.
2. Check whether it’s a prime.
3. If it is, store it in a list.
4. Check whether the list has ten elements.
5. If it has ten elements, return it as the result.
6. If not, increment the integer by 1.
7. Go to line 2.
Wait! I can achieve the same with a
strict algorithm
Yes, but how?
1. Take the first integer.
2. Check whether it’s a prime.
3. If it is, store it in a list.
4. Check whether the list has ten elements.
5. If it has ten elements, return it as the result.
6. If not, increment the integer by 1.
7. Go to line 2.
Sure, doable …
but what a mess!
Laziness lets us separate the description of an
expression from the evaluation of that expression
Laziness is an enabler for separation of concerns
List<String> errors =
Files.lines(Paths.get(fileName))
.filter(l -> l.startsWith("ERROR"))
.limit(40)
.collect(toList());
Separation of Concerns
List<String> errors = new ArrayList<>();
int errorCount = 0;
File file = new File(fileName);
String line = file.readLine();
while (errorCount < 40 && line != null) {
if (line.startsWith("ERROR")) {
errors.add(line);
errorCount++;
}
line = file.readLine();
}
Cool! Now I know:
I will use a Stream
also for prime
numbers
Cool! Now I know:
I will use a Stream
also for prime
numbers
Let’s give this a try ...
Creating a Stream of prime numbers
public IntStream primes(int n) {
return IntStream.iterate(2, i -> i + 1)
.filter(this::isPrime)
.limit(n);
}
public boolean isPrime(int candidate) {
return IntStream.rangeClosed(2, (int)Math.sqrt(candidate))
.noneMatch(i -> candidate % i == 0);
}
Creating a Stream of prime numbers
public IntStream primes(int n) {
return IntStream.iterate(2, i -> i + 1)
.filter(this::isPrime)
.limit(n);
}
public boolean isPrime(int candidate) {
return IntStream.rangeClosed(2, (int)Math.sqrt(candidate))
.noneMatch(i -> candidate % i == 0);
}
It iterates through every number every time to see if it can be
exactly divided by a candidate number, but it would be enough
to only test numbers that have been already classified as prime
Inefficient
Recursively creating a Stream of primes
static Intstream numbers() {
return IntStream.iterate(2, n -> n + 1);
}
static int head(IntStream numbers) {
return numbers.findFirst().getAsInt();
}
static IntStream tail(IntStream numbers) {
return numbers.skip(1);
}
static IntStream primes(IntStream numbers) {
int head = head(numbers());
return IntStream.concat(
IntStream.of(head),
primes(tail(numbers).filter(n -> n % head != 0))
);
}
Cannot invoke 2
terminal operations
on the same Streams
Problems?
No lazy evaluation
in Java leads to an
endless recursion
Lazy evaluation in Scala
def numbers(n: Int): Stream[Int] = n #:: numbers(n+1)
def primes(numbers: Stream[Int]): Stream[Int] =
numbers.head #::
primes(numbers.tail filter (n -> n % numbers.head != 0))
lazy concatenation
In Scala the #:: method (lazy concatenation)
returns immediately and the elements are
evaluated only when needed
interface HeadTailList<T> {
T head();
HeadTailList<T> tail();
boolean isEmpty();
HeadTailList<T> filter(Predicate<T> p);
}
Implementing a lazy list in Java
class LazyList<T> implements HeadTailList<T> {
private final T head;
private final Supplier<HeadTailList<T>> tail;
public LazyList(T head,
Supplier<HeadTailList<T>> tail) {
this.head = head;
this.tail = tail;
}
public T head() { return head; }
public HeadTailList<T> tail() { return tail.get(); }
public boolean isEmpty() { return false; }
}
… and its lazy filter
class LazyList<T> implements HeadTailList<T> { ...
public HeadTailList<T> filter(Predicate<T> p) {
return isEmpty() ?
this :
p.test(head()) ?
new LazyList<>(head(), () -> tail().filter(p)) :
tail().filter(p);
}
} 2
3 4
5
6
7
8
9
2
3
5
7
Back to generating primes
static HeadTailList<Integer> primes(HeadTailList<Integer> numbers) {
return new LazyList<>(
numbers.head(),
() -> primes(numbers.tail()
.filter(n -> n % numbers.head() != 0)));
}
static LazyList<Integer> from(int n) {
return new LazyList<Integer>(n, () -> from(n+1));
}
Back to generating primes
static HeadTailList<Integer> primes(HeadTailList<Integer> numbers) {
return new LazyList<>(
numbers.head(),
() -> primes(numbers.tail()
.filter(n -> n % numbers.head() != 0)));
}
static LazyList<Integer> from(int n) {
return new LazyList<Integer>(n, () -> from(n+1));
}
LazyList<Integer> numbers = from(2);
int two = primes(numbers).head();
int three = primes(numbers).tail().head();
int five = primes(numbers).tail().tail().head();
LazyList of primes under the hood
from(2) → 2 () -> from(3)
2 () -> primes( from(3).filter(2) )primes(from(2)) →
LazyList of primes under the hood
from(2) → 2 () -> from(3)
2 () -> primes( from(3).filter(2) )
3 () -> from(4).filter(2).filter(3)() -> primes( )
3 () -> primes( from(4).filter(2).filter(3) )
primes(from(2)) →
.tail() →
LazyList of primes under the hood
from(2) → 2 () -> from(3)
2 () -> primes( from(3).filter(2) )
3 () -> from(4).filter(2).filter(3)() -> primes( )
3 () -> primes( from(4).filter(2).filter(3) )
5 () -> from(6).filter(2).filter(3).filter(5)() -> primes( )
5 () -> primes( from(6).filter(2).filter(3).filter(5) )
primes(from(2)) →
.tail() →
.tail() →
Printing primes
static <T> void printAll(HeadTailList<T> list) {
while (!list.isEmpty()){
System.out.println(list.head());
list = list.tail();
}
}
printAll(primes(from(2)));
iteratively
Printing primes
static <T> void printAll(HeadTailList<T> list) {
while (!list.isEmpty()){
System.out.println(list.head());
list = list.tail();
}
}
printAll(primes(from(2)));
static <T> void printAll(HeadTailList<T> list) {
if (list.isEmpty()) return;
System.out.println(list.head());
printAll(list.tail());
}
printAll(primes(from(2)));
iteratively
recursively
Iteration vs. Recursion
External Iteration
public int sumAll(int n) {
int result = 0;
for (int i = 0; i <= n; i++) {
result += i;
}
return result;
}
Internal Iteration
public static int sumAll(int n) {
return IntStream.rangeClosed(0, n).sum();
}
Iteration vs. Recursion
External Iteration
public int sumAll(int n) {
int result = 0;
for (int i = 0; i <= n; i++) {
result += i;
}
return result;
}
Recursion
public int sumAll(int n) {
return n == 0 ? 0 : n + sumAll(n - 1);
}
Internal Iteration
public static int sumAll(int n) {
return IntStream.rangeClosed(0, n).sum();
}
public class PalindromePredicate implements Predicate<String> {
@Override public boolean test(String s) {
return isPalindrome(s, 0, s.length()-1);
}
private boolean isPalindrome(String s, int start, int end) {
while (start < end && !isLetter(s.charAt(start))) start++;
while (start < end && !isLetter(s.charAt(end))) end--;
if (start >= end) return true;
if (toLowerCase(s.charAt(start)) !=
toLowerCase(s.charAt(end))) return false;
return isPalindrome(s, start+1, end-1);
}
}
Another Recursive Example
Tail Rescursive Call
What's the problem?
List<String> sentences = asList( "Dammit, I’m mad!",
"Rise to vote, sir!",
"Never odd or even",
"Never odd and even",
"Was it a car or a cat I saw?",
"Was it a car or a dog I saw?",
VERY_LONG_PALINDROME );
sentences.stream()
.filter(new PalindromePredicate())
.forEach(System.out::println);
What's the problem?
List<String> sentences = asList( "Dammit, I’m mad!",
"Rise to vote, sir!",
"Never odd or even",
"Never odd and even",
"Was it a car or a cat I saw?",
"Was it a car or a dog I saw?",
VERY_LONG_PALINDROME );
sentences.stream()
.filter(new PalindromePredicate())
.forEach(System.out::println);
Exception in thread "main" java.lang.StackOverflowError
at java.lang.Character.getType(Character.java:6924)
at java.lang.Character.isLetter(Character.java:5798)
at java.lang.Character.isLetter(Character.java:5761)
at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:17)
at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:21)
at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:21)
at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:21)
……..
Tail Call Optimization
int func_a(int data) {
data = do_this(data);
return do_that(data);
} ... | executing inside func_a()
push EIP | push current instruction pointer on stack
push data | push variable 'data' on the stack
jmp do_this | call do_this() by jumping to its address
... | executing inside do_this()
push EIP | push current instruction pointer on stack
push data | push variable 'data' on the stack
jmp do_that | call do_that() by jumping to its address
... | executing inside do_that()
pop data | prepare to return value of 'data'
pop EIP | return to do_this()
pop data | prepare to return value of 'data'
pop EIP | return to func_a()
pop data | prepare to return value of 'data'
pop EIP | return to func_a() caller
...
caller
Tail Call Optimization
int func_a(int data) {
data = do_this(data);
return do_that(data);
} ... | executing inside func_a()
push EIP | push current instruction pointer on stack
push data | push variable 'data' on the stack
jmp do_this | call do_this() by jumping to its address
... | executing inside do_this()
push EIP | push current instruction pointer on stack
push data | push variable 'data' on the stack
jmp do_that | call do_that() by jumping to its address
... | executing inside do_that()
pop data | prepare to return value of 'data'
pop EIP | return to do_this()
pop data | prepare to return value of 'data'
pop EIP | return to func_a()
pop data | prepare to return value of 'data'
pop EIP | return to func_a() caller
...
caller
avoid putting
instruction
on stack
from Recursion to Tail Recursion
Recursion
public int sumAll(int n) {
return n == 0 ?
0 :
n + sumAll(n - 1);
}
from Recursion to Tail Recursion
Recursion
public int sumAll(int n) {
return n == 0 ?
0 :
n + sumAll(n - 1);
}
Tail Recursion
public int sumAll(int n) {
return sumAll(n, 0);
}
private int sumAll(int n, int acc) {
return n == 0 ?
acc :
sumAll(n – 1, acc + n);
}
Tail Recursion in Scala
def isPalindrome(s: String): Boolean = isPalindrome(s, 0, s.length-1)
@tailrec
def isPalindrome(s: String, start: Int, end: Int): Boolean = {
val pos1 = nextLetter(s, start, end)
val pos2 = prevLetter(s, start, end)
if (pos1 >= pos2) return true
if (toLowerCase(s.charAt(pos1)) !=
toLowerCase(s.charAt(pos2))) return false
isPalindrome(s, pos1+1, pos2-1)
}
@tailrec def nextLetter(s: String, start: Int, end: Int): Int =
if (start > end || isLetter(s.charAt(start))) start
else nextLetter(s, start+1, end)
@tailrec def prevLetter(s: String, start: Int, end: Int): Int =
if (start > end || isLetter(s.charAt(end))) end
else prevLetter(s, start, end-1)
Tail Recursion in Java?
Scala (and many other functional languages) automatically
perform tail call optimization at compile time
@tailrec annotation ensures the compiler will optimize a tail
recursive function (i.e. you will get a compilation failure if you
use it on a function that is not really tail recursive)
Java compiler doesn't perform any tail call optimization (and
very likely won't do it in a near future)
How can we overcome this limitation and
have StackOverflowError-free functions also
in Java tail recursive methods?
Trampolines to the rescue
A trampoline is an iteration applying a list of functions.
Each function returns the next function for the loop to run.
Func1
return
apply
Func2
return
apply
Func3
return
apply
FuncN
apply
…
result
return
Implementing the TailCall …
@FunctionalInterface
public interface TailCall<T> extends Supplier<TailCall<T>> {
default boolean isComplete() { return false; }
default T result() { throw new UnsupportedOperationException(); }
default T invoke() {
return Stream.iterate(this, TailCall::get)
.filter(TailCall::isComplete)
.findFirst()
.get()
.result();
}
static <T> TailCall<T> done(T result) {
return new TerminalCall<T>(result);
}
}
… and the terminal TailCall
public class TerminalCall<T> implements TailCall<T> {
private final T result;
public TerminalCall( T result ) {
this.result = result;
}
@Override
public boolean isComplete() { return true; }
@Override
public T result() { return result; }
@Override
public TailCall<T> get() {
throw new UnsupportedOperationException();
}
}
Using the Trampoline
public class PalindromePredicate implements Predicate<String> {
@Override public boolean test(String s) {
return isPalindrome(s, 0, s.length()-1).invoke();
}
private TailCall<Boolean> isPalindrome(String s, int start,
int end) {
while (start < end && !isLetter(s.charAt(start))) start++;
while (end > start && !isLetter(s.charAt(end))) end--;
if (start >= end) return done(true);
if (toLowerCase(s.charAt(start)) !=
toLowerCase(s.charAt(end))) return done(false);
int newStart = start + 1;
int newEnd = end - 1;
return () -> isPalindrome(s, newStart, newEnd);
}
}
What else laziness can do for us?
Avoiding eager dependency injection by
lazily providing arguments to
computation only when they are needed
i.e.
Introducing the Reader Monad
What’s wrong with annotation-
based dependency injection?
➢ Eager in nature
➢ “new” keyword is forbidden
➢ All-your-beans-belong-to-us
syndrome
➢ Complicated objects lifecycle
➢ Depending on scope may
not work well with threads
➢ Hard to debug if something
goes wrong
➢ Easy to abuse leading to
broken encapsulation
Annotation based
dependency injection
transforms what
should be a compile
time problem into a
runtime one
(often hard to debug)
Introducing the Reader monad ...
public class Reader<R, A> {
private final Function<R, A> exec;
public Reader( Function<R, A> exec ) {
this.exec = exec;
}
public <B> Reader<R, B> map(Function<A, B> f) {
return new Reader<>( exec.andThen(f) );
}
public <B> Reader<R, B> flatMap(Function<A, Reader<R, B>> f) {
return new Reader<>( r -> exec.andThen(f).apply(r).apply(r) );
}
public A apply(R r) {
return exec.apply( r );
}
}
The reader monad provides an environment to
wrap an abstract computation without evaluating it
The Reader Monad
The Reader monad makes
a lazy computation
explicit
in the type system, while
hiding
the logic to apply it
In other words the reader monad
allows us to treat functions as
values with a context
We can act as if we already know
what the functions will return.
@FunctionalInterface
public interface Logger extends Consumer<String> { }
public class Account {
private Logger logger;
private String owner;
private double balance;
public Account open( String owner ) {
this.owner = owner;
logger.accept( "Account opened by " + owner );
return this;
}
public Account credit( double value ) {
balance += value;
logger.accept( "Credited " + value + " to " + owner );
return this;
}
public Account debit( double value ) {
balance -= value;
logger.accept( "Debited " + value + " to " + owner );
return this;
}
public double getBalance() { return balance; }
public void setLogger( Logger logger ) { this.logger = logger; }
}
Usually injected
Account account = new Account();
account.open( "Alice" )
.credit( 200.0 )
.credit( 300.0 )
.debit( 400.0 );
The joys of dependency injection
Account account = new Account();
account.open( "Alice" )
.credit( 200.0 )
.credit( 300.0 )
.debit( 400.0 );
Throws NPE if for some
reason the logger
couldn’t be injected
The joys of dependency injection :(
You should never use “new”
public static <R, A> Reader<R, A> lift( A obj,
BiConsumer<A, R> injector ) {
return new Reader<>( r -> {
injector.accept( obj, r );
return obj;
} );
}
Lazy injection with the reader monad
public static <R, A> Reader<R, A> lift( A obj,
BiConsumer<A, R> injector ) {
return new Reader<>( r -> {
injector.accept( obj, r );
return obj;
} );
}
Lazy injection with the reader monad
Account account = new Account();
Reader<Logger, Account> reader =
lift(account, Account::setLogger )
.map( a -> a.open( "Alice" ) )
.map( a -> a.credit( 200.0 ) )
.map( a -> a.credit( 300.0 ) )
.map( a -> a.debit( 400.0 ) );
reader.apply( System.out::println );
System.out.println(account + " has balance " + account.getBalance());
public static <R, A> Reader<R, A> lift( A obj,
BiConsumer<A, R> injector ) {
return new Reader<>( r -> {
injector.accept( obj, r );
return obj;
} );
}
Lazy injection with the reader monad
Account account = new Account();
Reader<Logger, Account> reader =
lift(account, Account::setLogger )
.map( a -> a.open( "Alice" ) )
.map( a -> a.credit( 200.0 ) )
.map( a -> a.credit( 300.0 ) )
.map( a -> a.debit( 400.0 ) );
reader.apply( System.out::println );
System.out.println(account + " has balance " + account.getBalance());
Replacing injection with
function application
Account
Function<Logger, Account>
Reader
Function<Logger, Account>
Reader
Function<Logger, Account>
Reader
Function<Logger, Account>
Reader
Function<Logger, Account>
Reader
lift
apply(logger)
account
map(Function<Account, Account>)
map(Function<Account, Account>)
Function based injection
Account account = new Account();
Function<Logger, Account> inject = l -> {
account.setLogger( l );
return account;
};
Function<Logger, Account> f = inject
.andThen( a -> a.open( "Alice" ) )
.andThen( a -> a.credit( 200.0 ) )
.andThen( a -> a.credit( 300.0 ) )
.andThen( a -> a.debit( 400.0 ) );
f.apply( System.out::println );
System.out.println(account + " has balance " + account.getBalance());
The reader monad provides a more
structured and powerful approach.
In this simple case a simple function composition
is enough to achieve the same result.
public class Account { ...
public MoneyTransfer tranfer( double value ) {
return new MoneyTransfer( this, value );
}
}
public class MoneyTransfer {
private Logger logger;
private final Account account;
private final double amount;
public MoneyTransfer( Account account, double amount ) {
this.account = account;
this.amount = amount;
}
public void setLogger( Logger logger ) { this.logger = logger; }
public MoneyTransfer execute() {
account.debit( amount );
logger.accept( "Transferred " + amount + " from " + account );
return this;
}
}
Injecting into multiple objects
Injecting into multiple objects
Account account = new Account();
Reader<Logger, MoneyTransfer> reader =
lift(account, Account::setLogger )
.map( a -> a.open( "Alice" ) )
.map( a -> a.credit( 300.0 ) )
.flatMap( a -> lift( a.tranfer( 200.0 ), MoneyTransfer::setLogger ) )
.map( MoneyTransfer::execute );
reader.apply( System.out::println );
System.out.println(account + " has balance " + account.getBalance());
Injecting into multiple objects
Account account = new Account();
Reader<Logger, MoneyTransfer> reader =
lift(account, Account::setLogger )
.map( a -> a.open( "Alice" ) )
.map( a -> a.credit( 300.0 ) )
.flatMap( a -> lift( a.tranfer( 200.0 ), MoneyTransfer::setLogger ) )
.map( MoneyTransfer::execute );
reader.apply( System.out::println );
System.out.println(account + " has balance " + account.getBalance());
Mario Fusco
Red Hat – Principal Software Engineer
mario.fusco@gmail.com
twitter: @mariofusco
Q A
Thanks … Questions?

More Related Content

What's hot (20)

Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
John Ferguson Smart Limited
 
Java - Collections framework
Java - Collections frameworkJava - Collections framework
Java - Collections framework
Riccardo Cardin
 
Introduction To Django
Introduction To DjangoIntroduction To Django
Introduction To Django
Jay Graves
 
Java swing
Java swingJava swing
Java swing
Apurbo Datta
 
Java 8 Lambda and Streams
Java 8 Lambda and StreamsJava 8 Lambda and Streams
Java 8 Lambda and Streams
Venkata Naga Ravi
 
A Basic Django Introduction
A Basic Django IntroductionA Basic Django Introduction
A Basic Django Introduction
Ganga Ram
 
Core java concepts
Core java  conceptsCore java  concepts
Core java concepts
Ram132
 
Overloading and overriding in vb.net
Overloading and overriding in vb.netOverloading and overriding in vb.net
Overloading and overriding in vb.net
suraj pandey
 
Collections In Java
Collections In JavaCollections In Java
Collections In Java
Binoj T E
 
Java 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional InterfacesJava 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional Interfaces
Ganesh Samarthyam
 
HTML CSS & Javascript
HTML CSS & JavascriptHTML CSS & Javascript
HTML CSS & Javascript
David Lindkvist
 
Loops PHP 04
Loops PHP 04Loops PHP 04
Loops PHP 04
Spy Seat
 
Java Method, Static Block
Java Method, Static BlockJava Method, Static Block
Java Method, Static Block
Infoviaan Technologies
 
React js
React jsReact js
React js
Oswald Campesato
 
Function
FunctionFunction
Function
jayesh30sikchi
 
Selenium IDE LOCATORS
Selenium IDE LOCATORSSelenium IDE LOCATORS
Selenium IDE LOCATORS
Mindfire Solutions
 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
Adieu
 
JAVA OOP
JAVA OOPJAVA OOP
JAVA OOP
Sunil OS
 
Angular 8
Angular 8 Angular 8
Angular 8
Sunil OS
 
Asynchronous API in Java8, how to use CompletableFuture
Asynchronous API in Java8, how to use CompletableFutureAsynchronous API in Java8, how to use CompletableFuture
Asynchronous API in Java8, how to use CompletableFuture
José Paumard
 
Java - Collections framework
Java - Collections frameworkJava - Collections framework
Java - Collections framework
Riccardo Cardin
 
Introduction To Django
Introduction To DjangoIntroduction To Django
Introduction To Django
Jay Graves
 
A Basic Django Introduction
A Basic Django IntroductionA Basic Django Introduction
A Basic Django Introduction
Ganga Ram
 
Core java concepts
Core java  conceptsCore java  concepts
Core java concepts
Ram132
 
Overloading and overriding in vb.net
Overloading and overriding in vb.netOverloading and overriding in vb.net
Overloading and overriding in vb.net
suraj pandey
 
Collections In Java
Collections In JavaCollections In Java
Collections In Java
Binoj T E
 
Java 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional InterfacesJava 8 Lambda Built-in Functional Interfaces
Java 8 Lambda Built-in Functional Interfaces
Ganesh Samarthyam
 
Loops PHP 04
Loops PHP 04Loops PHP 04
Loops PHP 04
Spy Seat
 
Advanced Javascript
Advanced JavascriptAdvanced Javascript
Advanced Javascript
Adieu
 
Angular 8
Angular 8 Angular 8
Angular 8
Sunil OS
 
Asynchronous API in Java8, how to use CompletableFuture
Asynchronous API in Java8, how to use CompletableFutureAsynchronous API in Java8, how to use CompletableFuture
Asynchronous API in Java8, how to use CompletableFuture
José Paumard
 

Similar to Lazy java (20)

Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
Mario Fusco
 
Good functional programming is good programming
Good functional programming is good programmingGood functional programming is good programming
Good functional programming is good programming
kenbot
 
Java 8 - functional features
Java 8 - functional featuresJava 8 - functional features
Java 8 - functional features
Rafal Rybacki
 
Functional Programming - Past, Present and Future
Functional Programming - Past, Present and FutureFunctional Programming - Past, Present and Future
Functional Programming - Past, Present and Future
Pushkar Kulkarni
 
Functional Programming Past Present Future
Functional Programming Past Present FutureFunctional Programming Past Present Future
Functional Programming Past Present Future
IndicThreads
 
Simpler java
Simpler javaSimpler java
Simpler java
Stefan von Stein
 
Harnessing the Power of Java 8 Streams
Harnessing the Power of Java 8 Streams Harnessing the Power of Java 8 Streams
Harnessing the Power of Java 8 Streams
IndicThreads
 
ADT02 - Java 8 Lambdas and the Streaming API
ADT02 - Java 8 Lambdas and the Streaming APIADT02 - Java 8 Lambdas and the Streaming API
ADT02 - Java 8 Lambdas and the Streaming API
Michael Remijan
 
Java 8 revealed
Java 8 revealedJava 8 revealed
Java 8 revealed
Sazzadur Rahaman
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Codemotion
 
OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...
OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...
OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...
Codemotion
 
OOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerOOP and FP - Become a Better Programmer
OOP and FP - Become a Better Programmer
Mario Fusco
 
Functional Programming in C#
Functional Programming in C#Functional Programming in C#
Functional Programming in C#
Giorgio Zoppi
 
FP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyondFP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyond
Mario Fusco
 
Functional Programming 101 for Java 7 Developers
Functional Programming 101 for Java 7 DevelopersFunctional Programming 101 for Java 7 Developers
Functional Programming 101 for Java 7 Developers
Jayaram Sankaranarayanan
 
Java 8 lambda expressions
Java 8 lambda expressionsJava 8 lambda expressions
Java 8 lambda expressions
Logan Chien
 
java 8 Hands on Workshop
java 8 Hands on Workshopjava 8 Hands on Workshop
java 8 Hands on Workshop
Jeanne Boyarsky
 
Functional Principles for OO Developers
Functional Principles for OO DevelopersFunctional Principles for OO Developers
Functional Principles for OO Developers
jessitron
 
The Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
Philip Schwarz
 
The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
Philip Schwarz
 
Good functional programming is good programming
Good functional programming is good programmingGood functional programming is good programming
Good functional programming is good programming
kenbot
 
Java 8 - functional features
Java 8 - functional featuresJava 8 - functional features
Java 8 - functional features
Rafal Rybacki
 
Functional Programming - Past, Present and Future
Functional Programming - Past, Present and FutureFunctional Programming - Past, Present and Future
Functional Programming - Past, Present and Future
Pushkar Kulkarni
 
Functional Programming Past Present Future
Functional Programming Past Present FutureFunctional Programming Past Present Future
Functional Programming Past Present Future
IndicThreads
 
Harnessing the Power of Java 8 Streams
Harnessing the Power of Java 8 Streams Harnessing the Power of Java 8 Streams
Harnessing the Power of Java 8 Streams
IndicThreads
 
ADT02 - Java 8 Lambdas and the Streaming API
ADT02 - Java 8 Lambdas and the Streaming APIADT02 - Java 8 Lambdas and the Streaming API
ADT02 - Java 8 Lambdas and the Streaming API
Michael Remijan
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Codemotion
 
OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...
OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...
OOP and FP: become a better programmer - Simone Bordet, Mario Fusco - Codemot...
Codemotion
 
OOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerOOP and FP - Become a Better Programmer
OOP and FP - Become a Better Programmer
Mario Fusco
 
Functional Programming in C#
Functional Programming in C#Functional Programming in C#
Functional Programming in C#
Giorgio Zoppi
 
FP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyondFP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyond
Mario Fusco
 
Functional Programming 101 for Java 7 Developers
Functional Programming 101 for Java 7 DevelopersFunctional Programming 101 for Java 7 Developers
Functional Programming 101 for Java 7 Developers
Jayaram Sankaranarayanan
 
Java 8 lambda expressions
Java 8 lambda expressionsJava 8 lambda expressions
Java 8 lambda expressions
Logan Chien
 
java 8 Hands on Workshop
java 8 Hands on Workshopjava 8 Hands on Workshop
java 8 Hands on Workshop
Jeanne Boyarsky
 
Functional Principles for OO Developers
Functional Principles for OO DevelopersFunctional Principles for OO Developers
Functional Principles for OO Developers
jessitron
 
The Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
Philip Schwarz
 
The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
Philip Schwarz
 
Ad

More from Mario Fusco (16)

Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automation
Mario Fusco
 
Let's make a contract: the art of designing a Java API
Let's make a contract: the art of designing a Java APILet's make a contract: the art of designing a Java API
Let's make a contract: the art of designing a Java API
Mario Fusco
 
How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...
Mario Fusco
 
OOP and FP
OOP and FPOOP and FP
OOP and FP
Mario Fusco
 
Drools 6 deep dive
Drools 6 deep diveDrools 6 deep dive
Drools 6 deep dive
Mario Fusco
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
Mario Fusco
 
Comparing different concurrency models on the JVM
Comparing different concurrency models on the JVMComparing different concurrency models on the JVM
Comparing different concurrency models on the JVM
Mario Fusco
 
Why we cannot ignore Functional Programming
Why we cannot ignore Functional ProgrammingWhy we cannot ignore Functional Programming
Why we cannot ignore Functional Programming
Mario Fusco
 
Real world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same languageReal world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same language
Mario Fusco
 
Introducing Drools
Introducing DroolsIntroducing Drools
Introducing Drools
Mario Fusco
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardJava 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
Mario Fusco
 
Hammurabi
HammurabiHammurabi
Hammurabi
Mario Fusco
 
Swiss army knife Spring
Swiss army knife SpringSwiss army knife Spring
Swiss army knife Spring
Mario Fusco
 
No more loops with lambdaj
No more loops with lambdajNo more loops with lambdaj
No more loops with lambdaj
Mario Fusco
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
Mario Fusco
 
Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automation
Mario Fusco
 
Let's make a contract: the art of designing a Java API
Let's make a contract: the art of designing a Java APILet's make a contract: the art of designing a Java API
Let's make a contract: the art of designing a Java API
Mario Fusco
 
How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...
Mario Fusco
 
Drools 6 deep dive
Drools 6 deep diveDrools 6 deep dive
Drools 6 deep dive
Mario Fusco
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
Mario Fusco
 
Comparing different concurrency models on the JVM
Comparing different concurrency models on the JVMComparing different concurrency models on the JVM
Comparing different concurrency models on the JVM
Mario Fusco
 
Why we cannot ignore Functional Programming
Why we cannot ignore Functional ProgrammingWhy we cannot ignore Functional Programming
Why we cannot ignore Functional Programming
Mario Fusco
 
Real world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same languageReal world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same language
Mario Fusco
 
Introducing Drools
Introducing DroolsIntroducing Drools
Introducing Drools
Mario Fusco
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardJava 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
Mario Fusco
 
Swiss army knife Spring
Swiss army knife SpringSwiss army knife Spring
Swiss army knife Spring
Mario Fusco
 
No more loops with lambdaj
No more loops with lambdajNo more loops with lambdaj
No more loops with lambdaj
Mario Fusco
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Mario Fusco
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
Mario Fusco
 
Ad

Recently uploaded (20)

How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdfHow to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
victordsane
 
Build enterprise-ready applications using skills you already have!
Build enterprise-ready applications using skills you already have!Build enterprise-ready applications using skills you already have!
Build enterprise-ready applications using skills you already have!
PhilMeredith3
 
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdfSecure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Northwind Technologies
 
Topic 26 Security Testing Considerations.pptx
Topic 26 Security Testing Considerations.pptxTopic 26 Security Testing Considerations.pptx
Topic 26 Security Testing Considerations.pptx
marutnand8
 
iOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod KumariOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod Kumar
Pramod Kumar
 
Rebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core FoundationRebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core Foundation
Cadabra Studio
 
Shortcomings of EHS Software – And How to Overcome Them
Shortcomings of EHS Software – And How to Overcome ThemShortcomings of EHS Software – And How to Overcome Them
Shortcomings of EHS Software – And How to Overcome Them
TECH EHS Solution
 
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptxHow AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
kalichargn70th171
 
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdfHow to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
QuickBooks Training
 
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATIONAI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
miso_uam
 
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdfICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
M. Luisetto Pharm.D.Spec. Pharmacology
 
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROIAutoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Udit Goenka
 
Online Queue Management System for Public Service Offices [Focused on Municip...
Online Queue Management System for Public Service Offices [Focused on Municip...Online Queue Management System for Public Service Offices [Focused on Municip...
Online Queue Management System for Public Service Offices [Focused on Municip...
Rishab Acharya
 
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternativesAI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative
 
Scalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple DevicesScalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple Devices
Scalefusion
 
Intranet Examples That Are Changing the Way We Work
Intranet Examples That Are Changing the Way We WorkIntranet Examples That Are Changing the Way We Work
Intranet Examples That Are Changing the Way We Work
BizPortals Solutions
 
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjaraswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
muhammadalikhanalikh1
 
Boost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for SchoolsBoost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for Schools
Visitu
 
SQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptxSQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptx
Ashlei5
 
The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...
Prachi Desai
 
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdfHow to purchase, license and subscribe to Microsoft Azure_PDF.pdf
How to purchase, license and subscribe to Microsoft Azure_PDF.pdf
victordsane
 
Build enterprise-ready applications using skills you already have!
Build enterprise-ready applications using skills you already have!Build enterprise-ready applications using skills you already have!
Build enterprise-ready applications using skills you already have!
PhilMeredith3
 
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdfSecure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Secure and Simplify IT Management with ManageEngine Endpoint Central.pdf
Northwind Technologies
 
Topic 26 Security Testing Considerations.pptx
Topic 26 Security Testing Considerations.pptxTopic 26 Security Testing Considerations.pptx
Topic 26 Security Testing Considerations.pptx
marutnand8
 
iOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod KumariOS Developer Resume 2025 | Pramod Kumar
iOS Developer Resume 2025 | Pramod Kumar
Pramod Kumar
 
Rebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core FoundationRebuilding Cadabra Studio: AI as Our Core Foundation
Rebuilding Cadabra Studio: AI as Our Core Foundation
Cadabra Studio
 
Shortcomings of EHS Software – And How to Overcome Them
Shortcomings of EHS Software – And How to Overcome ThemShortcomings of EHS Software – And How to Overcome Them
Shortcomings of EHS Software – And How to Overcome Them
TECH EHS Solution
 
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptxHow AI Can Improve Media Quality Testing Across Platforms (1).pptx
How AI Can Improve Media Quality Testing Across Platforms (1).pptx
kalichargn70th171
 
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdfHow to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
How to Generate Financial Statements in QuickBooks Like a Pro (1).pdf
QuickBooks Training
 
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATIONAI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
AI-ASSISTED METAMORPHIC TESTING FOR DOMAIN-SPECIFIC MODELLING AND SIMULATION
miso_uam
 
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdfICDL FULL STANDARD  2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
ICDL FULL STANDARD 2025 Luisetto mauro - Academia domani- 55 HOURS LONG pdf
M. Luisetto Pharm.D.Spec. Pharmacology
 
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROIAutoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Autoposting.ai Sales Deck - Skyrocket your LinkedIn's ROI
Udit Goenka
 
Online Queue Management System for Public Service Offices [Focused on Municip...
Online Queue Management System for Public Service Offices [Focused on Municip...Online Queue Management System for Public Service Offices [Focused on Municip...
Online Queue Management System for Public Service Offices [Focused on Municip...
Rishab Acharya
 
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternativesAI Alternative - Discover the best AI tools and their alternatives
AI Alternative - Discover the best AI tools and their alternatives
AI Alternative
 
Scalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple DevicesScalefusion Remote Access for Apple Devices
Scalefusion Remote Access for Apple Devices
Scalefusion
 
Intranet Examples That Are Changing the Way We Work
Intranet Examples That Are Changing the Way We WorkIntranet Examples That Are Changing the Way We Work
Intranet Examples That Are Changing the Way We Work
BizPortals Solutions
 
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjaraswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
aswjkdwelhjdfshlfjkhewljhfljawerhwjarhwjkahrjar
muhammadalikhanalikh1
 
Boost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for SchoolsBoost Student Engagement with Smart Attendance Software for Schools
Boost Student Engagement with Smart Attendance Software for Schools
Visitu
 
SQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptxSQL-COMMANDS instructionsssssssssss.pptx
SQL-COMMANDS instructionsssssssssss.pptx
Ashlei5
 
The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...The rise of e-commerce has redefined how retailers operate—and reconciliation...
The rise of e-commerce has redefined how retailers operate—and reconciliation...
Prachi Desai
 

Lazy java

  • 1. λazy by Mario Fusco Red Hat – Principal Software Engineer @mariofusco
  • 2. Lazy Evaluation Lazy evaluation (or call-by-name) is an evaluation strategy which delays the evaluation of an expression until its value is needed I know what to do. Wake me up when you really need it
  • 3. Strictness vs. Laziness Strictness is a property of functions (or methods in Java). A strict function always evaluates its arguments as soon as they’re passed to it. Conversely a lazy function may choose not to evaluate one or more of its arguments and in general it will evaluate them only when they’re actually needed. To recap, strictness is about doing things, laziness is about noting things to do.
  • 4. Java is a strict language ... … with some notable (and unavoidable) exceptions ✔ Boolean operators || and && ✔ Ternary operator ? : ✔ if ... else ✔ for/while loops ✔ Java 8 streams Q: Can exist a totally strict language?
  • 5. Java is a strict language ... … with some notable (and unavoidable) exceptions ✔ Boolean operators || and && ✔ Ternary operator ? : ✔ if ... else ✔ for/while loops ✔ Java 8 streams Q: Can exist a totally strict language? A: It’s hard if not impossible to imagine how it could work boolean isAdult = person != null && person.getAge() >= 18;
  • 6. Turning Java into a lazy language <T> T ternary(boolean pred, T first, T second) { if (pred) { return first; } else { return second; } } String val1() { return "first"; } String val2() { return "second"; } String result1 = bool ? val1() : val2(); String result2 = ternary(bool, val1(), val2());
  • 7. Turning Java into a lazy language <T> T ternary(boolean pred, Supplier<T> first, Supplier<T> second) { if (pred) { return first.get(); } else { return second.get(); } } String val1() { return "first"; } String val2() { return "second"; } String result1 = bool ? val1() : val2(); String result2 = ternary(bool, () -> val1(), () -> val2());
  • 8. A simple practical example: logging // pre-Java 8 style optimization if (logger.isTraceEnabled()) { logger.trace("Some long-running operation returned {}", expensiveOperation()); }
  • 9. A simple practical example: logging // pre-Java 8 style optimization if (logger.isTraceEnabled()) { logger.trace("Some long-running operation returned {}", expensiveOperation()); } // Java 8 style optimization using laziness logger.trace("Some long-running operation returned {}", () -> expensiveOperation()); * from Apache Log4J 2 docs no need to explicitly check the log level: the lambda expression is not evaluated if the TRACE level is not enabled *
  • 10. Laziness: the ultimate performance optimization technique Performance optimization pro tip: before trying to inline/optimize/parallelize a piece of code, ask yourself if you could avoid to run it at all. Laziness is probably the only form of performance optimization which is (almost) never premature There is nothing so useless as doing efficiently something that should not be done at all
  • 11. The case of Java 8 Streams IntStream.iterate( 1, i -> i+1 ) .map( i -> i * 2 ) .filter( i -> i > 5 ) .findFirst();
  • 12. The case of Java 8 Streams IntStream.iterate( 1, i -> i+1 ) .map( i -> i * 2 ) .filter( i -> i > 5 ) .findFirst(); Thank to laziness the Stream can be potentially infinite
  • 13. The case of Java 8 Streams IntStream.iterate( 1, i -> i+1 ) .map( i -> i * 2 ) .filter( i -> i > 5 ) .findFirst(); Thank to laziness the Stream can be potentially infinite Intermediate operations are lazy: they don’t perform any action until a terminal operation is reached
  • 14. The case of Java 8 Streams IntStream.iterate( 1, i -> i+1 ) .map( i -> i * 2 ) .filter( i -> i > 5 ) .findFirst(); Thank to laziness the Stream can be potentially infinite Intermediate operations are lazy: they don’t perform any action until a terminal operation is reached Only the terminal operation triggers the pipeline of computations A Stream is not a data structure. It is the lazy specification of a how to manipulate a set of data.
  • 15. Things you can’t do without laziness There are several algorithms that can’t be (reasonably) implemented without laziness. For example let’s consider the following: 1. Take the list of positive integers. 2. Filter the primes. 3. Return the list of the first ten results.
  • 16. Wait! I can achieve the same with a strict algorithm Yes, but how? 1. Take the first integer. 2. Check whether it’s a prime. 3. If it is, store it in a list. 4. Check whether the list has ten elements. 5. If it has ten elements, return it as the result. 6. If not, increment the integer by 1. 7. Go to line 2.
  • 17. Wait! I can achieve the same with a strict algorithm Yes, but how? 1. Take the first integer. 2. Check whether it’s a prime. 3. If it is, store it in a list. 4. Check whether the list has ten elements. 5. If it has ten elements, return it as the result. 6. If not, increment the integer by 1. 7. Go to line 2. Sure, doable … but what a mess!
  • 18. Laziness lets us separate the description of an expression from the evaluation of that expression Laziness is an enabler for separation of concerns
  • 19. List<String> errors = Files.lines(Paths.get(fileName)) .filter(l -> l.startsWith("ERROR")) .limit(40) .collect(toList()); Separation of Concerns List<String> errors = new ArrayList<>(); int errorCount = 0; File file = new File(fileName); String line = file.readLine(); while (errorCount < 40 && line != null) { if (line.startsWith("ERROR")) { errors.add(line); errorCount++; } line = file.readLine(); }
  • 20. Cool! Now I know: I will use a Stream also for prime numbers
  • 21. Cool! Now I know: I will use a Stream also for prime numbers Let’s give this a try ...
  • 22. Creating a Stream of prime numbers public IntStream primes(int n) { return IntStream.iterate(2, i -> i + 1) .filter(this::isPrime) .limit(n); } public boolean isPrime(int candidate) { return IntStream.rangeClosed(2, (int)Math.sqrt(candidate)) .noneMatch(i -> candidate % i == 0); }
  • 23. Creating a Stream of prime numbers public IntStream primes(int n) { return IntStream.iterate(2, i -> i + 1) .filter(this::isPrime) .limit(n); } public boolean isPrime(int candidate) { return IntStream.rangeClosed(2, (int)Math.sqrt(candidate)) .noneMatch(i -> candidate % i == 0); } It iterates through every number every time to see if it can be exactly divided by a candidate number, but it would be enough to only test numbers that have been already classified as prime Inefficient
  • 24. Recursively creating a Stream of primes static Intstream numbers() { return IntStream.iterate(2, n -> n + 1); } static int head(IntStream numbers) { return numbers.findFirst().getAsInt(); } static IntStream tail(IntStream numbers) { return numbers.skip(1); } static IntStream primes(IntStream numbers) { int head = head(numbers()); return IntStream.concat( IntStream.of(head), primes(tail(numbers).filter(n -> n % head != 0)) ); } Cannot invoke 2 terminal operations on the same Streams Problems? No lazy evaluation in Java leads to an endless recursion
  • 25. Lazy evaluation in Scala def numbers(n: Int): Stream[Int] = n #:: numbers(n+1) def primes(numbers: Stream[Int]): Stream[Int] = numbers.head #:: primes(numbers.tail filter (n -> n % numbers.head != 0)) lazy concatenation In Scala the #:: method (lazy concatenation) returns immediately and the elements are evaluated only when needed
  • 26. interface HeadTailList<T> { T head(); HeadTailList<T> tail(); boolean isEmpty(); HeadTailList<T> filter(Predicate<T> p); } Implementing a lazy list in Java class LazyList<T> implements HeadTailList<T> { private final T head; private final Supplier<HeadTailList<T>> tail; public LazyList(T head, Supplier<HeadTailList<T>> tail) { this.head = head; this.tail = tail; } public T head() { return head; } public HeadTailList<T> tail() { return tail.get(); } public boolean isEmpty() { return false; } }
  • 27. … and its lazy filter class LazyList<T> implements HeadTailList<T> { ... public HeadTailList<T> filter(Predicate<T> p) { return isEmpty() ? this : p.test(head()) ? new LazyList<>(head(), () -> tail().filter(p)) : tail().filter(p); } } 2 3 4 5 6 7 8 9 2 3 5 7
  • 28. Back to generating primes static HeadTailList<Integer> primes(HeadTailList<Integer> numbers) { return new LazyList<>( numbers.head(), () -> primes(numbers.tail() .filter(n -> n % numbers.head() != 0))); } static LazyList<Integer> from(int n) { return new LazyList<Integer>(n, () -> from(n+1)); }
  • 29. Back to generating primes static HeadTailList<Integer> primes(HeadTailList<Integer> numbers) { return new LazyList<>( numbers.head(), () -> primes(numbers.tail() .filter(n -> n % numbers.head() != 0))); } static LazyList<Integer> from(int n) { return new LazyList<Integer>(n, () -> from(n+1)); } LazyList<Integer> numbers = from(2); int two = primes(numbers).head(); int three = primes(numbers).tail().head(); int five = primes(numbers).tail().tail().head();
  • 30. LazyList of primes under the hood from(2) → 2 () -> from(3) 2 () -> primes( from(3).filter(2) )primes(from(2)) →
  • 31. LazyList of primes under the hood from(2) → 2 () -> from(3) 2 () -> primes( from(3).filter(2) ) 3 () -> from(4).filter(2).filter(3)() -> primes( ) 3 () -> primes( from(4).filter(2).filter(3) ) primes(from(2)) → .tail() →
  • 32. LazyList of primes under the hood from(2) → 2 () -> from(3) 2 () -> primes( from(3).filter(2) ) 3 () -> from(4).filter(2).filter(3)() -> primes( ) 3 () -> primes( from(4).filter(2).filter(3) ) 5 () -> from(6).filter(2).filter(3).filter(5)() -> primes( ) 5 () -> primes( from(6).filter(2).filter(3).filter(5) ) primes(from(2)) → .tail() → .tail() →
  • 33. Printing primes static <T> void printAll(HeadTailList<T> list) { while (!list.isEmpty()){ System.out.println(list.head()); list = list.tail(); } } printAll(primes(from(2))); iteratively
  • 34. Printing primes static <T> void printAll(HeadTailList<T> list) { while (!list.isEmpty()){ System.out.println(list.head()); list = list.tail(); } } printAll(primes(from(2))); static <T> void printAll(HeadTailList<T> list) { if (list.isEmpty()) return; System.out.println(list.head()); printAll(list.tail()); } printAll(primes(from(2))); iteratively recursively
  • 35. Iteration vs. Recursion External Iteration public int sumAll(int n) { int result = 0; for (int i = 0; i <= n; i++) { result += i; } return result; } Internal Iteration public static int sumAll(int n) { return IntStream.rangeClosed(0, n).sum(); }
  • 36. Iteration vs. Recursion External Iteration public int sumAll(int n) { int result = 0; for (int i = 0; i <= n; i++) { result += i; } return result; } Recursion public int sumAll(int n) { return n == 0 ? 0 : n + sumAll(n - 1); } Internal Iteration public static int sumAll(int n) { return IntStream.rangeClosed(0, n).sum(); }
  • 37. public class PalindromePredicate implements Predicate<String> { @Override public boolean test(String s) { return isPalindrome(s, 0, s.length()-1); } private boolean isPalindrome(String s, int start, int end) { while (start < end && !isLetter(s.charAt(start))) start++; while (start < end && !isLetter(s.charAt(end))) end--; if (start >= end) return true; if (toLowerCase(s.charAt(start)) != toLowerCase(s.charAt(end))) return false; return isPalindrome(s, start+1, end-1); } } Another Recursive Example Tail Rescursive Call
  • 38. What's the problem? List<String> sentences = asList( "Dammit, I’m mad!", "Rise to vote, sir!", "Never odd or even", "Never odd and even", "Was it a car or a cat I saw?", "Was it a car or a dog I saw?", VERY_LONG_PALINDROME ); sentences.stream() .filter(new PalindromePredicate()) .forEach(System.out::println);
  • 39. What's the problem? List<String> sentences = asList( "Dammit, I’m mad!", "Rise to vote, sir!", "Never odd or even", "Never odd and even", "Was it a car or a cat I saw?", "Was it a car or a dog I saw?", VERY_LONG_PALINDROME ); sentences.stream() .filter(new PalindromePredicate()) .forEach(System.out::println); Exception in thread "main" java.lang.StackOverflowError at java.lang.Character.getType(Character.java:6924) at java.lang.Character.isLetter(Character.java:5798) at java.lang.Character.isLetter(Character.java:5761) at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:17) at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:21) at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:21) at org.javaz.trampoline.PalindromePredicate.isPalindrome(PalindromePredicate.java:21) ……..
  • 40. Tail Call Optimization int func_a(int data) { data = do_this(data); return do_that(data); } ... | executing inside func_a() push EIP | push current instruction pointer on stack push data | push variable 'data' on the stack jmp do_this | call do_this() by jumping to its address ... | executing inside do_this() push EIP | push current instruction pointer on stack push data | push variable 'data' on the stack jmp do_that | call do_that() by jumping to its address ... | executing inside do_that() pop data | prepare to return value of 'data' pop EIP | return to do_this() pop data | prepare to return value of 'data' pop EIP | return to func_a() pop data | prepare to return value of 'data' pop EIP | return to func_a() caller ... caller
  • 41. Tail Call Optimization int func_a(int data) { data = do_this(data); return do_that(data); } ... | executing inside func_a() push EIP | push current instruction pointer on stack push data | push variable 'data' on the stack jmp do_this | call do_this() by jumping to its address ... | executing inside do_this() push EIP | push current instruction pointer on stack push data | push variable 'data' on the stack jmp do_that | call do_that() by jumping to its address ... | executing inside do_that() pop data | prepare to return value of 'data' pop EIP | return to do_this() pop data | prepare to return value of 'data' pop EIP | return to func_a() pop data | prepare to return value of 'data' pop EIP | return to func_a() caller ... caller avoid putting instruction on stack
  • 42. from Recursion to Tail Recursion Recursion public int sumAll(int n) { return n == 0 ? 0 : n + sumAll(n - 1); }
  • 43. from Recursion to Tail Recursion Recursion public int sumAll(int n) { return n == 0 ? 0 : n + sumAll(n - 1); } Tail Recursion public int sumAll(int n) { return sumAll(n, 0); } private int sumAll(int n, int acc) { return n == 0 ? acc : sumAll(n – 1, acc + n); }
  • 44. Tail Recursion in Scala def isPalindrome(s: String): Boolean = isPalindrome(s, 0, s.length-1) @tailrec def isPalindrome(s: String, start: Int, end: Int): Boolean = { val pos1 = nextLetter(s, start, end) val pos2 = prevLetter(s, start, end) if (pos1 >= pos2) return true if (toLowerCase(s.charAt(pos1)) != toLowerCase(s.charAt(pos2))) return false isPalindrome(s, pos1+1, pos2-1) } @tailrec def nextLetter(s: String, start: Int, end: Int): Int = if (start > end || isLetter(s.charAt(start))) start else nextLetter(s, start+1, end) @tailrec def prevLetter(s: String, start: Int, end: Int): Int = if (start > end || isLetter(s.charAt(end))) end else prevLetter(s, start, end-1)
  • 45. Tail Recursion in Java? Scala (and many other functional languages) automatically perform tail call optimization at compile time @tailrec annotation ensures the compiler will optimize a tail recursive function (i.e. you will get a compilation failure if you use it on a function that is not really tail recursive) Java compiler doesn't perform any tail call optimization (and very likely won't do it in a near future) How can we overcome this limitation and have StackOverflowError-free functions also in Java tail recursive methods?
  • 46. Trampolines to the rescue A trampoline is an iteration applying a list of functions. Each function returns the next function for the loop to run. Func1 return apply Func2 return apply Func3 return apply FuncN apply … result return
  • 47. Implementing the TailCall … @FunctionalInterface public interface TailCall<T> extends Supplier<TailCall<T>> { default boolean isComplete() { return false; } default T result() { throw new UnsupportedOperationException(); } default T invoke() { return Stream.iterate(this, TailCall::get) .filter(TailCall::isComplete) .findFirst() .get() .result(); } static <T> TailCall<T> done(T result) { return new TerminalCall<T>(result); } }
  • 48. … and the terminal TailCall public class TerminalCall<T> implements TailCall<T> { private final T result; public TerminalCall( T result ) { this.result = result; } @Override public boolean isComplete() { return true; } @Override public T result() { return result; } @Override public TailCall<T> get() { throw new UnsupportedOperationException(); } }
  • 49. Using the Trampoline public class PalindromePredicate implements Predicate<String> { @Override public boolean test(String s) { return isPalindrome(s, 0, s.length()-1).invoke(); } private TailCall<Boolean> isPalindrome(String s, int start, int end) { while (start < end && !isLetter(s.charAt(start))) start++; while (end > start && !isLetter(s.charAt(end))) end--; if (start >= end) return done(true); if (toLowerCase(s.charAt(start)) != toLowerCase(s.charAt(end))) return done(false); int newStart = start + 1; int newEnd = end - 1; return () -> isPalindrome(s, newStart, newEnd); } }
  • 50. What else laziness can do for us? Avoiding eager dependency injection by lazily providing arguments to computation only when they are needed i.e. Introducing the Reader Monad
  • 51. What’s wrong with annotation- based dependency injection? ➢ Eager in nature ➢ “new” keyword is forbidden ➢ All-your-beans-belong-to-us syndrome ➢ Complicated objects lifecycle ➢ Depending on scope may not work well with threads ➢ Hard to debug if something goes wrong ➢ Easy to abuse leading to broken encapsulation
  • 52. Annotation based dependency injection transforms what should be a compile time problem into a runtime one (often hard to debug)
  • 53. Introducing the Reader monad ... public class Reader<R, A> { private final Function<R, A> exec; public Reader( Function<R, A> exec ) { this.exec = exec; } public <B> Reader<R, B> map(Function<A, B> f) { return new Reader<>( exec.andThen(f) ); } public <B> Reader<R, B> flatMap(Function<A, Reader<R, B>> f) { return new Reader<>( r -> exec.andThen(f).apply(r).apply(r) ); } public A apply(R r) { return exec.apply( r ); } } The reader monad provides an environment to wrap an abstract computation without evaluating it
  • 54. The Reader Monad The Reader monad makes a lazy computation explicit in the type system, while hiding the logic to apply it In other words the reader monad allows us to treat functions as values with a context We can act as if we already know what the functions will return.
  • 55. @FunctionalInterface public interface Logger extends Consumer<String> { } public class Account { private Logger logger; private String owner; private double balance; public Account open( String owner ) { this.owner = owner; logger.accept( "Account opened by " + owner ); return this; } public Account credit( double value ) { balance += value; logger.accept( "Credited " + value + " to " + owner ); return this; } public Account debit( double value ) { balance -= value; logger.accept( "Debited " + value + " to " + owner ); return this; } public double getBalance() { return balance; } public void setLogger( Logger logger ) { this.logger = logger; } } Usually injected
  • 56. Account account = new Account(); account.open( "Alice" ) .credit( 200.0 ) .credit( 300.0 ) .debit( 400.0 ); The joys of dependency injection
  • 57. Account account = new Account(); account.open( "Alice" ) .credit( 200.0 ) .credit( 300.0 ) .debit( 400.0 ); Throws NPE if for some reason the logger couldn’t be injected The joys of dependency injection :( You should never use “new”
  • 58. public static <R, A> Reader<R, A> lift( A obj, BiConsumer<A, R> injector ) { return new Reader<>( r -> { injector.accept( obj, r ); return obj; } ); } Lazy injection with the reader monad
  • 59. public static <R, A> Reader<R, A> lift( A obj, BiConsumer<A, R> injector ) { return new Reader<>( r -> { injector.accept( obj, r ); return obj; } ); } Lazy injection with the reader monad Account account = new Account(); Reader<Logger, Account> reader = lift(account, Account::setLogger ) .map( a -> a.open( "Alice" ) ) .map( a -> a.credit( 200.0 ) ) .map( a -> a.credit( 300.0 ) ) .map( a -> a.debit( 400.0 ) ); reader.apply( System.out::println ); System.out.println(account + " has balance " + account.getBalance());
  • 60. public static <R, A> Reader<R, A> lift( A obj, BiConsumer<A, R> injector ) { return new Reader<>( r -> { injector.accept( obj, r ); return obj; } ); } Lazy injection with the reader monad Account account = new Account(); Reader<Logger, Account> reader = lift(account, Account::setLogger ) .map( a -> a.open( "Alice" ) ) .map( a -> a.credit( 200.0 ) ) .map( a -> a.credit( 300.0 ) ) .map( a -> a.debit( 400.0 ) ); reader.apply( System.out::println ); System.out.println(account + " has balance " + account.getBalance());
  • 61. Replacing injection with function application Account Function<Logger, Account> Reader Function<Logger, Account> Reader Function<Logger, Account> Reader Function<Logger, Account> Reader Function<Logger, Account> Reader lift apply(logger) account map(Function<Account, Account>) map(Function<Account, Account>)
  • 62. Function based injection Account account = new Account(); Function<Logger, Account> inject = l -> { account.setLogger( l ); return account; }; Function<Logger, Account> f = inject .andThen( a -> a.open( "Alice" ) ) .andThen( a -> a.credit( 200.0 ) ) .andThen( a -> a.credit( 300.0 ) ) .andThen( a -> a.debit( 400.0 ) ); f.apply( System.out::println ); System.out.println(account + " has balance " + account.getBalance()); The reader monad provides a more structured and powerful approach. In this simple case a simple function composition is enough to achieve the same result.
  • 63. public class Account { ... public MoneyTransfer tranfer( double value ) { return new MoneyTransfer( this, value ); } } public class MoneyTransfer { private Logger logger; private final Account account; private final double amount; public MoneyTransfer( Account account, double amount ) { this.account = account; this.amount = amount; } public void setLogger( Logger logger ) { this.logger = logger; } public MoneyTransfer execute() { account.debit( amount ); logger.accept( "Transferred " + amount + " from " + account ); return this; } } Injecting into multiple objects
  • 64. Injecting into multiple objects Account account = new Account(); Reader<Logger, MoneyTransfer> reader = lift(account, Account::setLogger ) .map( a -> a.open( "Alice" ) ) .map( a -> a.credit( 300.0 ) ) .flatMap( a -> lift( a.tranfer( 200.0 ), MoneyTransfer::setLogger ) ) .map( MoneyTransfer::execute ); reader.apply( System.out::println ); System.out.println(account + " has balance " + account.getBalance());
  • 65. Injecting into multiple objects Account account = new Account(); Reader<Logger, MoneyTransfer> reader = lift(account, Account::setLogger ) .map( a -> a.open( "Alice" ) ) .map( a -> a.credit( 300.0 ) ) .flatMap( a -> lift( a.tranfer( 200.0 ), MoneyTransfer::setLogger ) ) .map( MoneyTransfer::execute ); reader.apply( System.out::println ); System.out.println(account + " has balance " + account.getBalance());
  • 66. Mario Fusco Red Hat – Principal Software Engineer [email protected] twitter: @mariofusco Q A Thanks … Questions?