SlideShare ist ein Scribd-Unternehmen logo
1 von 66
Downloaden Sie, um offline zu lesen
λ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?

Weitere ähnliche Inhalte

Was ist angesagt?

Functional Thinking - Programming with Lambdas in Java 8
Functional Thinking - Programming with Lambdas in Java 8Functional Thinking - Programming with Lambdas in Java 8
Functional Thinking - Programming with Lambdas in Java 8Ganesh Samarthyam
 
Java Generics Introduction - Syntax Advantages and Pitfalls
Java Generics Introduction - Syntax Advantages and PitfallsJava Generics Introduction - Syntax Advantages and Pitfalls
Java Generics Introduction - Syntax Advantages and PitfallsRakesh Waghela
 
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
 
Pj01 4-operators and control flow
Pj01 4-operators and control flowPj01 4-operators and control flow
Pj01 4-operators and control flowSasidharaRaoMarrapu
 
Java Generics for Dummies
Java Generics for DummiesJava Generics for Dummies
Java Generics for Dummiesknutmork
 
Clone Refactoring with Lambda Expressions
Clone Refactoring with Lambda ExpressionsClone Refactoring with Lambda Expressions
Clone Refactoring with Lambda ExpressionsNikolaos Tsantalis
 
Effective Java - Generics
Effective Java - GenericsEffective Java - Generics
Effective Java - GenericsRoshan Deniyage
 
Java best practices
Java best practicesJava best practices
Java best practicesRay Toal
 
02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LAB02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LABHari Christian
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentationVan Huong
 
Python Programming - II. The Basics
Python Programming - II. The BasicsPython Programming - II. The Basics
Python Programming - II. The BasicsRanel Padon
 

Was ist angesagt? (17)

Java operators
Java operatorsJava operators
Java operators
 
Functional Thinking - Programming with Lambdas in Java 8
Functional Thinking - Programming with Lambdas in Java 8Functional Thinking - Programming with Lambdas in Java 8
Functional Thinking - Programming with Lambdas in Java 8
 
Java generics
Java genericsJava generics
Java generics
 
Java Generics Introduction - Syntax Advantages and Pitfalls
Java Generics Introduction - Syntax Advantages and PitfallsJava Generics Introduction - Syntax Advantages and Pitfalls
Java Generics Introduction - Syntax Advantages and Pitfalls
 
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
 
Pj01 4-operators and control flow
Pj01 4-operators and control flowPj01 4-operators and control flow
Pj01 4-operators and control flow
 
Java Generics for Dummies
Java Generics for DummiesJava Generics for Dummies
Java Generics for Dummies
 
Clone Refactoring with Lambda Expressions
Clone Refactoring with Lambda ExpressionsClone Refactoring with Lambda Expressions
Clone Refactoring with Lambda Expressions
 
Effective Java - Generics
Effective Java - GenericsEffective Java - Generics
Effective Java - Generics
 
Java best practices
Java best practicesJava best practices
Java best practices
 
02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LAB02 Java Language And OOP Part II LAB
02 Java Language And OOP Part II LAB
 
Java 8 presentation
Java 8 presentationJava 8 presentation
Java 8 presentation
 
Java 8 Lambda Expressions
Java 8 Lambda ExpressionsJava 8 Lambda Expressions
Java 8 Lambda Expressions
 
Java 8 ​and ​Best Practices
Java 8 ​and ​Best PracticesJava 8 ​and ​Best Practices
Java 8 ​and ​Best Practices
 
Java programming-examples
Java programming-examplesJava programming-examples
Java programming-examples
 
Python Programming - II. The Basics
Python Programming - II. The BasicsPython Programming - II. The Basics
Python Programming - II. The Basics
 
Generic Programming
Generic ProgrammingGeneric Programming
Generic Programming
 

Ähnlich wie Lazy Java

Automation Testing theory notes.pptx
Automation Testing theory notes.pptxAutomation Testing theory notes.pptx
Automation Testing theory notes.pptxNileshBorkar12
 
Lambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeLambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeIan Robertson
 
Functional Programming In Java
Functional Programming In JavaFunctional Programming In Java
Functional Programming In JavaAndrei Solntsev
 
object oriented programming java lectures
object oriented programming java lecturesobject oriented programming java lectures
object oriented programming java lecturesMSohaib24
 
New Functional Features of Java 8
New Functional Features of Java 8New Functional Features of Java 8
New Functional Features of Java 8franciscoortin
 
JAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingJAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingKeshav Kumar
 
JAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingJAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingKeshav Kumar
 
Functions, List and String methods
Functions, List and String methodsFunctions, List and String methods
Functions, List and String methodsPranavSB
 
computer notes - Data Structures - 5
computer notes - Data Structures - 5computer notes - Data Structures - 5
computer notes - Data Structures - 5ecomputernotes
 

Ähnlich wie Lazy Java (20)

Core java
Core javaCore java
Core java
 
Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
 
Scala ntnu
Scala ntnuScala ntnu
Scala ntnu
 
Java tut1
Java tut1Java tut1
Java tut1
 
Tutorial java
Tutorial javaTutorial java
Tutorial java
 
Java Tut1
Java Tut1Java Tut1
Java Tut1
 
Java Tutorial
Java TutorialJava Tutorial
Java Tutorial
 
Automation Testing theory notes.pptx
Automation Testing theory notes.pptxAutomation Testing theory notes.pptx
Automation Testing theory notes.pptx
 
Java 8 new features
Java 8 new featuresJava 8 new features
Java 8 new features
 
Lambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive CodeLambda Chops - Recipes for Simpler, More Expressive Code
Lambda Chops - Recipes for Simpler, More Expressive Code
 
Functional Programming In Java
Functional Programming In JavaFunctional Programming In Java
Functional Programming In Java
 
object oriented programming java lectures
object oriented programming java lecturesobject oriented programming java lectures
object oriented programming java lectures
 
New Functional Features of Java 8
New Functional Features of Java 8New Functional Features of Java 8
New Functional Features of Java 8
 
Wien15 java8
Wien15 java8Wien15 java8
Wien15 java8
 
JAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingJAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programming
 
JAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programmingJAVA Tutorial- Do's and Don'ts of Java programming
JAVA Tutorial- Do's and Don'ts of Java programming
 
java8
java8java8
java8
 
Functions, List and String methods
Functions, List and String methodsFunctions, List and String methods
Functions, List and String methods
 
computer notes - Data Structures - 5
computer notes - Data Structures - 5computer notes - Data Structures - 5
computer notes - Data Structures - 5
 
Lambda Functions in Java 8
Lambda Functions in Java 8Lambda Functions in Java 8
Lambda Functions in Java 8
 

Mehr von Nicola Pedot

AI, ML e l'anello mancante
AI, ML e l'anello mancanteAI, ML e l'anello mancante
AI, ML e l'anello mancanteNicola Pedot
 
Say No To Dependency Hell
Say No To Dependency Hell Say No To Dependency Hell
Say No To Dependency Hell Nicola Pedot
 
Java al servizio della data science - Java developers' meeting
Java al servizio della data science - Java developers' meetingJava al servizio della data science - Java developers' meeting
Java al servizio della data science - Java developers' meetingNicola Pedot
 
Java 9-10 What's New
Java 9-10 What's NewJava 9-10 What's New
Java 9-10 What's NewNicola Pedot
 
BDD & design paradoxes appunti devoxx2012
BDD & design paradoxes   appunti devoxx2012BDD & design paradoxes   appunti devoxx2012
BDD & design paradoxes appunti devoxx2012Nicola Pedot
 
Tom EE appunti devoxx2012
Tom EE   appunti devoxx2012 Tom EE   appunti devoxx2012
Tom EE appunti devoxx2012 Nicola Pedot
 
Presentazione+Android
Presentazione+AndroidPresentazione+Android
Presentazione+AndroidNicola Pedot
 

Mehr von Nicola Pedot (13)

AI, ML e l'anello mancante
AI, ML e l'anello mancanteAI, ML e l'anello mancante
AI, ML e l'anello mancante
 
Ethic clean
Ethic cleanEthic clean
Ethic clean
 
Say No To Dependency Hell
Say No To Dependency Hell Say No To Dependency Hell
Say No To Dependency Hell
 
Java al servizio della data science - Java developers' meeting
Java al servizio della data science - Java developers' meetingJava al servizio della data science - Java developers' meeting
Java al servizio della data science - Java developers' meeting
 
Jakarta EE 2018
Jakarta EE 2018Jakarta EE 2018
Jakarta EE 2018
 
Java 9-10 What's New
Java 9-10 What's NewJava 9-10 What's New
Java 9-10 What's New
 
JavaEE6 my way
JavaEE6 my wayJavaEE6 my way
JavaEE6 my way
 
Java 8 Overview
Java 8 OverviewJava 8 Overview
Java 8 Overview
 
BDD & design paradoxes appunti devoxx2012
BDD & design paradoxes   appunti devoxx2012BDD & design paradoxes   appunti devoxx2012
BDD & design paradoxes appunti devoxx2012
 
Tom EE appunti devoxx2012
Tom EE   appunti devoxx2012 Tom EE   appunti devoxx2012
Tom EE appunti devoxx2012
 
Eclipse Svn
Eclipse SvnEclipse Svn
Eclipse Svn
 
Eclipse
EclipseEclipse
Eclipse
 
Presentazione+Android
Presentazione+AndroidPresentazione+Android
Presentazione+Android
 

Kürzlich hochgeladen

Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...
Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...
Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...marjmae69
 
Genesis part 2 Isaiah Scudder 04-24-2024.pptx
Genesis part 2 Isaiah Scudder 04-24-2024.pptxGenesis part 2 Isaiah Scudder 04-24-2024.pptx
Genesis part 2 Isaiah Scudder 04-24-2024.pptxFamilyWorshipCenterD
 
Work Remotely with Confluence ACE 2.pptx
Work Remotely with Confluence ACE 2.pptxWork Remotely with Confluence ACE 2.pptx
Work Remotely with Confluence ACE 2.pptxmavinoikein
 
PHYSICS PROJECT BY MSC - NANOTECHNOLOGY
PHYSICS PROJECT BY MSC  - NANOTECHNOLOGYPHYSICS PROJECT BY MSC  - NANOTECHNOLOGY
PHYSICS PROJECT BY MSC - NANOTECHNOLOGYpruthirajnayak525
 
THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...
THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...
THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...漢銘 謝
 
miladyskindiseases-200705210221 2.!!pptx
miladyskindiseases-200705210221 2.!!pptxmiladyskindiseases-200705210221 2.!!pptx
miladyskindiseases-200705210221 2.!!pptxCarrieButtitta
 
The Ten Facts About People With Autism Presentation
The Ten Facts About People With Autism PresentationThe Ten Facts About People With Autism Presentation
The Ten Facts About People With Autism PresentationNathan Young
 
Event 4 Introduction to Open Source.pptx
Event 4 Introduction to Open Source.pptxEvent 4 Introduction to Open Source.pptx
Event 4 Introduction to Open Source.pptxaryanv1753
 
James Joyce, Dubliners and Ulysses.ppt !
James Joyce, Dubliners and Ulysses.ppt !James Joyce, Dubliners and Ulysses.ppt !
James Joyce, Dubliners and Ulysses.ppt !risocarla2016
 
The 3rd Intl. Workshop on NL-based Software Engineering
The 3rd Intl. Workshop on NL-based Software EngineeringThe 3rd Intl. Workshop on NL-based Software Engineering
The 3rd Intl. Workshop on NL-based Software EngineeringSebastiano Panichella
 
SaaStr Workshop Wednesday w/ Kyle Norton, Owner.com
SaaStr Workshop Wednesday w/ Kyle Norton, Owner.comSaaStr Workshop Wednesday w/ Kyle Norton, Owner.com
SaaStr Workshop Wednesday w/ Kyle Norton, Owner.comsaastr
 
call girls in delhi malviya nagar @9811711561@
call girls in delhi malviya nagar @9811711561@call girls in delhi malviya nagar @9811711561@
call girls in delhi malviya nagar @9811711561@vikas rana
 
Dutch Power - 26 maart 2024 - Henk Kras - Circular Plastics
Dutch Power - 26 maart 2024 - Henk Kras - Circular PlasticsDutch Power - 26 maart 2024 - Henk Kras - Circular Plastics
Dutch Power - 26 maart 2024 - Henk Kras - Circular PlasticsDutch Power
 
PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.
PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.
PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.KathleenAnnCordero2
 
Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...
Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...
Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...Krijn Poppe
 
Anne Frank A Beacon of Hope amidst darkness ppt.pptx
Anne Frank A Beacon of Hope amidst darkness ppt.pptxAnne Frank A Beacon of Hope amidst darkness ppt.pptx
Anne Frank A Beacon of Hope amidst darkness ppt.pptxnoorehahmad
 
Genshin Impact PPT Template by EaTemp.pptx
Genshin Impact PPT Template by EaTemp.pptxGenshin Impact PPT Template by EaTemp.pptx
Genshin Impact PPT Template by EaTemp.pptxJohnree4
 
SBFT Tool Competition 2024 -- Python Test Case Generation Track
SBFT Tool Competition 2024 -- Python Test Case Generation TrackSBFT Tool Competition 2024 -- Python Test Case Generation Track
SBFT Tool Competition 2024 -- Python Test Case Generation TrackSebastiano Panichella
 
Simulation-based Testing of Unmanned Aerial Vehicles with Aerialist
Simulation-based Testing of Unmanned Aerial Vehicles with AerialistSimulation-based Testing of Unmanned Aerial Vehicles with Aerialist
Simulation-based Testing of Unmanned Aerial Vehicles with AerialistSebastiano Panichella
 
Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝soniya singh
 

Kürzlich hochgeladen (20)

Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...
Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...
Gaps, Issues and Challenges in the Implementation of Mother Tongue Based-Mult...
 
Genesis part 2 Isaiah Scudder 04-24-2024.pptx
Genesis part 2 Isaiah Scudder 04-24-2024.pptxGenesis part 2 Isaiah Scudder 04-24-2024.pptx
Genesis part 2 Isaiah Scudder 04-24-2024.pptx
 
Work Remotely with Confluence ACE 2.pptx
Work Remotely with Confluence ACE 2.pptxWork Remotely with Confluence ACE 2.pptx
Work Remotely with Confluence ACE 2.pptx
 
PHYSICS PROJECT BY MSC - NANOTECHNOLOGY
PHYSICS PROJECT BY MSC  - NANOTECHNOLOGYPHYSICS PROJECT BY MSC  - NANOTECHNOLOGY
PHYSICS PROJECT BY MSC - NANOTECHNOLOGY
 
THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...
THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...
THE COUNTRY WHO SOLVED THE WORLD_HOW CHINA LAUNCHED THE CIVILIZATION REVOLUTI...
 
miladyskindiseases-200705210221 2.!!pptx
miladyskindiseases-200705210221 2.!!pptxmiladyskindiseases-200705210221 2.!!pptx
miladyskindiseases-200705210221 2.!!pptx
 
The Ten Facts About People With Autism Presentation
The Ten Facts About People With Autism PresentationThe Ten Facts About People With Autism Presentation
The Ten Facts About People With Autism Presentation
 
Event 4 Introduction to Open Source.pptx
Event 4 Introduction to Open Source.pptxEvent 4 Introduction to Open Source.pptx
Event 4 Introduction to Open Source.pptx
 
James Joyce, Dubliners and Ulysses.ppt !
James Joyce, Dubliners and Ulysses.ppt !James Joyce, Dubliners and Ulysses.ppt !
James Joyce, Dubliners and Ulysses.ppt !
 
The 3rd Intl. Workshop on NL-based Software Engineering
The 3rd Intl. Workshop on NL-based Software EngineeringThe 3rd Intl. Workshop on NL-based Software Engineering
The 3rd Intl. Workshop on NL-based Software Engineering
 
SaaStr Workshop Wednesday w/ Kyle Norton, Owner.com
SaaStr Workshop Wednesday w/ Kyle Norton, Owner.comSaaStr Workshop Wednesday w/ Kyle Norton, Owner.com
SaaStr Workshop Wednesday w/ Kyle Norton, Owner.com
 
call girls in delhi malviya nagar @9811711561@
call girls in delhi malviya nagar @9811711561@call girls in delhi malviya nagar @9811711561@
call girls in delhi malviya nagar @9811711561@
 
Dutch Power - 26 maart 2024 - Henk Kras - Circular Plastics
Dutch Power - 26 maart 2024 - Henk Kras - Circular PlasticsDutch Power - 26 maart 2024 - Henk Kras - Circular Plastics
Dutch Power - 26 maart 2024 - Henk Kras - Circular Plastics
 
PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.
PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.
PAG-UNLAD NG EKONOMIYA na dapat isaalang alang sa pag-aaral.
 
Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...
Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...
Presentation for the Strategic Dialogue on the Future of Agriculture, Brussel...
 
Anne Frank A Beacon of Hope amidst darkness ppt.pptx
Anne Frank A Beacon of Hope amidst darkness ppt.pptxAnne Frank A Beacon of Hope amidst darkness ppt.pptx
Anne Frank A Beacon of Hope amidst darkness ppt.pptx
 
Genshin Impact PPT Template by EaTemp.pptx
Genshin Impact PPT Template by EaTemp.pptxGenshin Impact PPT Template by EaTemp.pptx
Genshin Impact PPT Template by EaTemp.pptx
 
SBFT Tool Competition 2024 -- Python Test Case Generation Track
SBFT Tool Competition 2024 -- Python Test Case Generation TrackSBFT Tool Competition 2024 -- Python Test Case Generation Track
SBFT Tool Competition 2024 -- Python Test Case Generation Track
 
Simulation-based Testing of Unmanned Aerial Vehicles with Aerialist
Simulation-based Testing of Unmanned Aerial Vehicles with AerialistSimulation-based Testing of Unmanned Aerial Vehicles with Aerialist
Simulation-based Testing of Unmanned Aerial Vehicles with Aerialist
 
Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Rohini Delhi 💯Call Us 🔝8264348440🔝
 

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 mario.fusco@gmail.com twitter: @mariofusco Q A Thanks … Questions?