if you think you can stay away from functional programming, you are wrong
DESCRIPTION
TRANSCRIPT
by Mario [email protected]: @mariofusco
Moore's
law
The number of transistors on integrated
circuits doubles approximately every two years
Moore's
law
The number of transistors on integrated
circuits doubles approximately every two years
Now achieved
by increasing
the number
of cores
Moore's
law
The number of transistors on integrated
circuits doubles approximately every two years
Now achieved
by increasing
the number
of cores
Fortran
C / C++Java
Lisp
MLHaskell
Add abstractions
C#
Algol
Subtract abstractions
Imperative languages
Functional languages
Scala
F#
Hybrid languages
Amdahl's
law
The speedup of a program using multiple processors
in parallel computing is limited by the time needed
for the sequential fraction of the program
Concurrency & Parallelism
Parallel programmingRunning multiple tasks at
the same time
Concurrent programmingManaging concurrent requests
Both are hard!
The cause of the problem …
Mutable state +
Parallel processing =
Non-determinism
The cause of the problem …
Mutable state +
Parallel processing =
Non-determinism
Functional
Programming
Race conditions
Deadlocks
Starvation
Livelocks
… and its effects
Too hard to think about them!
Race conditions
Deadlocks
Starvation
Livelocks
… and its effects
The native Java concurrency model
Based on:
They are sometimes plain evil …
… and sometimes a necessary pain …
… but always the wrong default
Threads
Semaphores
SynchronizationLocks
Different concurrency models
Isolated mutable state (actors)
Purely immutable (pure functions)
Shared mutable state
(threads + locks)
Summing attendants ages (Threads)
class Blackboard {
int sum = 0;
int read() { return sum; }
void write(int value) { sum = value; }
}
class Attendant implements Runnable {
int age;
Blackboard blackboard;
public void run() {
synchronized(blackboard) {
int oldSum = blackboard.read();
int newSum = oldSum + age;
blackboard.write(newSum);
}
}
}
Summing attendants ages (Actors)
class Blackboard extends UntypedActors {
int sum = 0;
public void onReceive(Object message) {
if (message instanceof Integer) {
sum += (Integer)message;
}
}
} class Attendant {
int age;
Blackboard blackboard;
public void sendAge() {
blackboard.sendOneWay(age);
}
}
Summing attendants ages (Functional)
class Blackboard {
final int sum;
Blackboard(int sum) { this.sum = sum; }
}
class Attendant {
int age;
Attendant next;
public Blackboard addMyAge(Blackboard blackboard) {
final Blackboard b = new Blackboard(blackboard.sum + age);
return next == null ? b : next.myAge(b);
}
}
The state quadrantsMutable
Immutable
Shared
Unshared
Actors
Functional
Programming
Threads
Concurrency friendly
Concurrency unfriendly
OOP makes code understandable
by encapsulating moving parts
FP makes code understandable
by minimizing moving parts
- Michael Feathers
OOP vs FP
The OOP/FP dualism - OOPpublic class Bird { }
public class Cat {
private Bird catch;
private boolean full;
public void capture(Bird bird) {
catch = bird;
}
public void eat() {
full = true;
catch = null;
}
}
Cat cat = new Cat();
Bird bird = new Bird();
cat.capture(bird);
cat.eat();
The story
The OOP/FP dualism - FPpublic class Bird { }
public class Cat {
public CatWithCatch capture(Bird bird) { return new CatWithCatch(bird); }
}
public class CatWithCatch {
private final Bird catch;
public CatWithCatch(Bird bird) { catch = bird; }
public FullCat eat() { return new FullCat(); }
}
public class FullCat { }
BiFunction<Cat, Bird, FullCat> story =
((BiFunction<Cat, Bird, CatWithCatch>)Cat::capture)
.compose(CatWithCatch::eat);
FullCat fullCat = story.apply( new Cat(), new Bird() );
The OOP/FP dualism - FPpublic class Bird { }
public class Cat {
public CatWithCatch capture(Bird bird) { return new CatWithCatch(bird); }
}
public class CatWithCatch {
private final Bird catch;
public CatWithCatch(Bird bird) { catch = bird; }
public FullCat eat() { return new FullCat(); }
}
public class FullCat { }
BiFunction<Cat, Bird, FullCat> story =
((BiFunction<Cat, Bird, CatWithCatch>)Cat::capture)
.compose(CatWithCatch::eat);
FullCat fullCat = story.apply( new Cat(), new Bird() );
Immutability
Emphasis on verbs
instead of names
No need to test internal state: correctness enforced by the compiler
for (Employee e : employees) {
e.setSalary(e.getSalary() * 1.03);
}
employees.forEach(e -> e.setSalary(e.getSalary() * 1.03));
Inherently serial
Client has to manage iteration
Nested loops are poorly readable
+ Library is in control � opportunity for internal optimizations as parallelization,
lazy evaluation, out-of-order execution
+ More what, less how � better readability
+ Fluent (pipelined) operations � better readability
+ Client can pass behaviors into the API as data �
possibility to abstract and generalize over behavior �
more powerful, expressive APIs
Not only a syntactic change!
Internal vs External Iteration
� Encourages a pipelined ( "fluent" ) usage style
� Operations are divided between intermediate and terminal
� Lazy in nature: only terminal operations actually trigger a computation
employees.stream()
.filter(e -> e.getIncome() > 50000)
.map(e -> e.getName())
.forEach(System.out::println);
Streams - Efficiency with laziness
� Encourages a pipelined ( "fluent" ) usage style
� Operations are divided between intermediate and terminal
� Lazy in nature: only terminal operations actually trigger a computation
employees.stream()
.filter(e -> e.getIncome() > 50000)
.map(e -> e.getName())
.forEach(System.out::println);
parallelStream()
… and parallelism for free
Streams - Efficiency with laziness
Is there such thing as a free lunch?
Probably yes …
… but we need functional
forks and knives to eat it
�Reassigning a variable
�Modifying a data structure in place
� Setting a field on an object
� Throwing an exception or halting with an error
�Printing to the console
�Reading user input
�Reading from or writing to a file
�Drawing on the screen
A program created using only pure functions
What is a functional program?
No side effects allowed like:
Functional programming is a restriction on how we
write programs, but not on what they can do
}}
What is a (pure) function?
A function with input type A and output type B
is a computation which relates every value a of
type A to exactly one value b of type B such
that b is determined solely by the value of a
But, if it really is a
function, it will do
nothing else
Referential transparency
An expression e is referentially transparent if for all programs p,
all occurrences of e in p can be replaced by the result of
evaluating e, without affecting the observable behavior of p
A function f is pure if the expression f(x) is referentially
transparent for all referentially transparent x
RTString x = "purple";
String r1 = x.replace('p', 't');
String r2 = x.replace('p', 't');
String r1 = "purple".replace('p', 't');
r1: "turtle"
String r2 = "purple".replace('p', 't');
r2: "turtle"
StringBuilder x = new StringBuilder("Hi");
StringBuilder y = x.append(", mom");
String r1 = y.toString();
String r2 = y.toString();
String r1 = x.append(", mom").toString();
r1: "Hi, mom"
String r2 = x.append(", mom").toString();
r1: "Hi, mom, mom"
Non-RT
vs.
RT wins
�Under a developer point of view:
� Easier to reason about since effects of evaluation are
purely local
�Use of the substitution model: it's possible to replace a
term with an equivalent one
�Under a performance point of view:
� The JVM is free to optimize the code by safely reordering
the instructions
�No need to synchronize access to shared data
�Possible to cache the result of time consuming functions
(memoization), e.g. with
Map.computeIfAbsent(K key,
Function<? super K,? extends V> mappingFunction)
Mutability
�Parameter binding is about assigning names to things
�Mutating variables is about assigning things to names
Does that second
one sound weird?
… well it's because
it IS weird
Immutability
� Immutable objects can be shared among
many threads exactly because none of
them can modify it
� In the same way immutable (persistent)
data structures can be shared without any
need to synchronize the different threads
accessing them
5
8
7 9
3
4
EEEEE E
E
Persistent Collections
5
8
7 9
3
4
EEEEE E
E
2
Persistent Collections
5
8
7 9
3
4
EEEEE E
E
5
3
2
EE
Persistent Collections
5
8
7 9
3
4
EEEEE E
E
5
3
2
EE
Shared data
Old Root
New Root
Persistent Collections
Modularityclass Player {
String name;
int score;
}
public void declareWinner(Player p) {
System.out.println(p.name + " wins!");
}
public void winner(Player p1, Player p2) {
if (p1.score > p2.score) declareWinner(p1)
else declareWinner(p2);
}
Modularityclass Player {
String name;
int score;
}
public void declareWinner(Player p) {
System.out.println(p.name + " wins!");
}
public void winner(Player p1, Player p2) {
if (p1.score > p2.score) declareWinner(p1)
else declareWinner(p2);
}
public Player maxScore(Player p1, Player p2) {
return p1.score > p2.score ? p1 : p2;
}
public void winner(Player p1, Player p2) {
declareWinner(maxScore(p1, p2));
}
Separate
computational logic
from side effects
Modularityclass Player {
String name;
int score;
}
public void declareWinner(Player p) {
System.out.println(p.name + " wins!");
}
public void winner(Player p1, Player p2) {
if (p1.score > p2.score) declareWinner(p1)
else declareWinner(p2);
}
public Player maxScore(Player p1, Player p2) {
return p1.score > p2.score ? p1 : p2;
}
public void winner(Player p1, Player p2) {
declareWinner(maxScore(p1, p2));
}
Separate
computational logic
from side effects
declareWinner(players.reduce(maxScore).get())
reuse maxScore to compute the winner among a list of players
A pure functional core
functional
core
Any function with side-effects can be split into a pure function at the
core and a pair of functions with side-effect. This transformation can
be repeated to push side-effects to the outer layers of the program.
Key Takeaways
� Strive for immutability
� Confine side-effects
� Avoid blocking code
� Cede control with higher-order functions
� Leverage referential transparency
� Use FP to design more composable and reusable API
… but there are no dogmas
� Be pragmatic and use the right tool for the job at hand
Poly-paradigm programming is more powerful
and effective than polyglot programming
Mario Fusco
Red Hat – Senior Software Engineer
twitter: @mariofusco
Q A
Thanks … Questions?