14. Actually I made up the term "object-oriented",
and I can tell you I did not have C++ in mind.
Alan Kay
15. The way OOP is
implemented in most
common imperative
languages is
probably the biggest
misunderstanding
in the millenarian
history of engineering
This is Class Oriented Programming
16. A Smalltalk object can do exactly three things:
OOP == Message Passing
✔ Hold state (references to
other objects).
✔ Receive a message from
itself or another object.
✔ In the course of processing a
message, send messages to
itself or another object.
17. A Smalltalk object can do exactly three things:
OOP == Message Passing
✔ Hold state (references to
other objects).
✔ Receive a message from
itself or another object.
✔ In the course of processing a
message, send messages to
itself or another object.
That’s the Actor Model !!!
18. Functional Programming Myths
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
19. Functional Programming Myths
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
20. Functional Programming Myths
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
✔ FP is the opposite of OOP: they are orthogonal and can
be effectively mixed in any program
21. Functional Programming Myths
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
✔ FP is the opposite of OOP: they are orthogonal and can
be effectively mixed in any program
✔ FP is hard: OOP is not easier, just more familiar
22. Functional Programming Myths
✔ FP is less efficient: it’s the compiler job to produce efficient code
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
✔ FP is the opposite of OOP: they are orthogonal and can
be effectively mixed in any program
✔ FP is hard: OOP is not easier, just more familiar
23. Functional Programming Myths
✔ FP is less efficient: it’s the compiler job to produce efficient code
✔ FP is good only for math-like stuff: it is good for any task
including GUIs, think to functional reactive programming
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
✔ FP is the opposite of OOP: they are orthogonal and can
be effectively mixed in any program
✔ FP is hard: OOP is not easier, just more familiar
24. Functional Programming Myths
✔ FP is less efficient: it’s the compiler job to produce efficient code
✔ FP is good only for math-like stuff: it is good for any task
including GUIs, think to functional reactive programming
✔ FP is an all or nothing game: you can choose the amount of FP
that is right for you according to your task, skills and tastes
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
✔ FP is the opposite of OOP: they are orthogonal and can
be effectively mixed in any program
✔ FP is hard: OOP is not easier, just more familiar
25. Functional Programming Myths
✔ FP is less efficient: it’s the compiler job to produce efficient code
✔ FP is good only for math-like stuff: it is good for any task
including GUIs, think to functional reactive programming
✔ FP is an all or nothing game: you can choose the amount of FP
that is right for you according to your task, skills and tastes
✔ FP solves all problems: oh yes, you can write horrible code also
doing pure functional programming
✔ FP is new: it is older than computer, origining from 1930s
lambda calculus. Haskell is 5 years older than Java
✔ FP is well-defined: are lambdas enough? Should it follow
the mathemathical definition? Something in the middle?
✔ FP is the opposite of OOP: they are orthogonal and can
be effectively mixed in any program
✔ FP is hard: OOP is not easier, just more familiar
26. Object Oriented Programming
Object-oriented programming (OOP) represents the concept
of objects that have data fields (attributes that describe the
object) and associated procedures known as methods.
Definitions
Functional Programming
Functional programming (FP) treats computation as
the evaluation and composition of mathematical
functions and avoids changing-state and mutable data.
27. What’s the difference?
All programs are functions operating on data
OOP and FP seems to differ in the manner in
which they bind functions and data together
FP: f(o)OOP: o.f()
28. What’s the difference?
(f o)
All programs are functions operating on data
OOP and FP seems to differ in the manner in
which they bind functions and data together
FP: f(o)OOP: o.f() But does it really matter?
In reality it is just
Functions ARE data, do you remember map/reduce?
But does it really matter?
29. What’s the difference?
(f o)
All programs are functions operating on data
polymorphism
OOP and FP seems to differ in the manner in
which they bind functions and data together
FP: f(o)OOP: o.f() But does it really matter?
In reality it is just
Functions ARE data, do you remember map/reduce?
But does it really matter?
30. Biggest OOP advantage: Polymorphism
Polymorphism is THE thing that truly
differentiates OO programs from non-OO ones
It is true that it can be achieved in FP
with long if/else chains, or better with
pattern matching
Polymorphism does NOT create a
source code dependency from the
caller to the callee
BUT in OOP
31. OOP vs FP Decomposition
eval toString hasZero ...
Int
Addition
Negation
...
32. OOP vs FP Decomposition
In OOP, break programs down
into classes that give
behaviour to some kind of data
eval toString hasZero ...
Int
Addition
Negation
...
fill the grid with
one class per row
33. OOP vs FP Decomposition
In FP, break programs down into
functions that perform some
operations over its arguments
In OOP, break programs down
into classes that give
behaviour to some kind of data
eval toString hasZero ...
Int
Addition
Negation
...
fill the grid with
one function per column
fill the grid with
one class per row
34. OOP Decomposition
public interface Expression {
int eval();
boolean hasZero();
String toString();
}
public class Int implements Expression {
private final int i;
public Int( int i ) { this.i = i; }
@Override
public int eval() { return i; }
@Override
public boolean hasZero() { return i == 0; }
@Override
public String toString() { return "" + i; }
}
public class Addition implements Expression {
private final Expression e1;
private final Expression e2;
public Addition( Expression e1, Expression e2 ) {
this.e1 = e1;
this.e2 = e2;
}
@Override
public int eval() {
return e1.eval() + e2.eval();
}
@Override
public boolean hasZero() {
return e1.hasZero() || e2.hasZero();
}
@Override
public String toString() {
return e1.toString() + " + " + e1.toString();
}
}
public class Negation implements Expression {
private final Expression e;
public Negation( Expression e ) { this.e = e; }
@Override
public int eval() { return - e.eval(); }
@Override
public boolean hasZero() { return e.hasZero(); }
@Override
public String toString() { return "-" + e.toString(); }
}
35. FP Decomposition
public interface Expression { }
public class Int implements Expression {
final int i;
public Int( int i ) {
this.i = i;
}
}
public class Negation implements Expression {
final Expression e;
public Negation( Expression e ) {
this.e = e;
}
}
public class Addition implements Expression {
final Expression e1;
final Expression e2;
public Addition( Expression e1,
Expression e2 ) {
this.e1 = e1;
this.e2 = e2;
}
}
static int eval(Expression expr) {
if (expr instanceof Int)
return (( Int ) expr).i;
if (expr instanceof Negation)
return - eval((( Negation ) expr).e);
if (expr instanceof Addition)
return eval((( Addition ) expr).e1) + eval((( Addition ) expr).e2);
throw new UnsupportedOperationException( "Unknown type: " + expr.getClass());
}
static boolean hasZero(Expression expr) {
if (expr instanceof Int)
return (( Int ) expr).i == 0;
if (expr instanceof Negation)
return hasZero((( Negation ) expr).e);
if (expr instanceof Addition)
return hasZero((( Addition ) expr).e1) || hasZero((( Addition ) expr).e2);
throw new UnsupportedOperationException( "Unknown type: " + expr.getClass());
}
static String toString(Expression expr) {
if (expr instanceof Int)
return "" + (( Int ) expr).i;
if (expr instanceof Negation)
return "-" + toString((( Negation ) expr).e);
if (expr instanceof Addition)
return toString((( Addition ) expr).e1) + " + " +
toString((( Addition ) expr).e2);
throw new UnsupportedOperationException( "Unknown type: " + expr.getClass());
}
36. Which one is better?
To write an interpreter FP
decomposition is more
natural: I’m evaluating an
expression and I have
separate cases for the
different expression’s kinds
37. Which one is better?
To write an interpreter FP
decomposition is more
natural: I’m evaluating an
expression and I have
separate cases for the
different expression’s kinds
In a GUI there are lots of different things on
the screen with different behaviours and
response to user’s actions: better to keep
these behaviours together with the
graphical objects using OO decomposition
38. Which one is better?
It’s mainly a matter of
personal tastes and using
the tool that fits better
the problem at hand
To write an interpreter FP
decomposition is more
natural: I’m evaluating an
expression and I have
separate cases for the
different expression’s kinds
In a GUI there are lots of different things on
the screen with different behaviours and
response to user’s actions: better to keep
these behaviours together with the
graphical objects using OO decomposition
42. Composition(?)
OOP implements composition using 2 mechanisms:
1. Inheritance (IS-A)
2. Association (HAS-A), often implemented with
dependency injection
Functional composition
43. Biggest FP advantage: Immutability
✔ Thread-safety: immutable objects are inherently thread-
safe since race conditions are impossible
✔ Parallelizability: no race conditions implies also no need
for any synchronization
✔ Caching: the hardest problem in using a cache is
invalidation, but with immutable objects it is not necessary
✔ Correctness: immutability makes it easier to write, read
and reason about the code
✔ Consistency: once you are given an immutable object
and verify its state, you know it will always remain safe
✔ Better encapsulation: objects can always be passed by
reference and there is no need to have to worry about
solutions like defensive copying
44. OOP makes code understandable
by encapsulating moving parts
FP makes code understandable
by minimizing moving parts
- Michael Feathers
(im)mutability – OOP vs FP
... but this is only for historical reasons
nothing mandates that OOP implies mutability
45. 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
Referential transparency
47. 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)
Referential transparency
means no side-effects
48. OOP vs FP Error Management
public static int parseInt(String s) throws NumberFormatException
What’s wrong with this signature?
49. OOP vs FP Error Management
public static int parseInt(String s) throws NumberFormatException
What’s wrong with this signature?
1. There isn’t anything exceptional in a String that cannot be parsed into a number
2. Breaking referential transparency prevents use of memoization
public static OptionalInt parseInt(String s)
Map<String, OptionalInt> cache = new HashMap<>();
public OptionalInt memoizedParse(String s) {
return cache.computeIfAbsent(s, Integer::parseInt);
}
would allow memoization
50. ✔
Often abused, especially for flow control
✔
Checked Exceptions harm API
extensibility/modificability
✔
They plays very badly with lambdas syntax
✔
Not composable: in presence of multiple
errors only the first one is reported
✔
In the end just a GLORIFIED
MULTILEVEL GOTO
Use Exception only Exceptionally
Exceptions should be used only
for non-recoverable errors
51. OOP : FP = Imperative : Declarative
List<String> errors = new ArrayList<>();
int errorCount = 0;
BufferedReader file = new BufferedReader(new FileReader(fileName));
String line = file.readLine();
while (errorCount < 40 && line != null) {
if (line.startsWith("ERROR")) {
errors.add(line);
errorCount++;
}
line = file.readLine();
}
List<String> errors =
Files.lines(Paths.get(fileName))
.filter(l -> l.startsWith("ERROR"))
.limit(40)
.collect(toList());
61. Threads vs (Completable)Future
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
62. Threads vs (Completable)Future
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
private double result = 1.0;
public synchronized void processResult(double value) {
result *= value;
}
public double getPriceUsingThreads(String productCode,
String currency)
throws InterruptedException {
Thread t1 = new Thread( new Runnable() {
@Override
public void run() {
processResult( loadConversionRate( "USD", currency ) );
}
} );
Thread t2 = new Thread( new Runnable() {
@Override
public void run() {
processResult( loadUSDPrice( productCode ) );
}
} );
t1.start();
t2.start();
t1.join();
t2.join();
return result;
}
63. Threads vs (Completable)Future
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
private double result = 1.0;
public synchronized void processResult(double value) {
result *= value;
}
public double getPriceUsingThreads(String productCode,
String currency)
throws InterruptedException {
Thread t1 = new Thread( new Runnable() {
@Override
public void run() {
processResult( loadConversionRate( "USD", currency ) );
}
} );
Thread t2 = new Thread( new Runnable() {
@Override
public void run() {
processResult( loadUSDPrice( productCode ) );
}
} );
t1.start();
t2.start();
t1.join();
t2.join();
return result;
}
64. Threads vs (Completable)Future
The problem with object-oriented
languages is they’ve got all this implicit
environment that they carry around
with them. You wanted a banana but
what you got was a gorilla holding the
banana and the entire jungle.
- Joe Armstrong
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
private double result = 1.0;
public synchronized void processResult(double value) {
result *= value;
}
public double getPriceUsingThreads(String productCode,
String currency)
throws InterruptedException {
Thread t1 = new Thread( new Runnable() {
@Override
public void run() {
processResult( loadConversionRate( "USD", currency ) );
}
} );
Thread t2 = new Thread( new Runnable() {
@Override
public void run() {
processResult( loadUSDPrice( productCode ) );
}
} );
t1.start();
t2.start();
t1.join();
t2.join();
return result;
}
65. Threads vs (Completable)Future
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
66. Threads vs (Completable)Future
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
public double getPriceUsingFutures(String productCode, String currency) throws ExecutionException, InterruptedException {
return CompletableFuture.supplyAsync( () -> loadUSDPrice( productCode ) )
.thenCombine( CompletableFuture.supplyAsync( () -> loadConversionRate( "USD", currency ) ),
(price, rate) -> price * rate ).get();
}
67. Threads vs (Completable)Future
public double loadConversionRate(String currency1,
String currency2) {
return Double.NaN; // TODO – some long computation
}
public double loadUSDPrice(String productCode) {
return Double.NaN; // TODO – some long computation
}
public double getPrice(String productCode, String currency) {
return loadUSDPrice( productCode ) *
loadConversionRate( "USD", currency );
}
public double getPriceUsingFutures(String productCode, String currency) throws ExecutionException, InterruptedException {
return CompletableFuture.supplyAsync( () -> loadUSDPrice( productCode ) )
.thenCombine( CompletableFuture.supplyAsync( () -> loadConversionRate( "USD", currency ) ),
(price, rate) -> price * rate ).get();
}
68. Expressions vs Statements
Object result = condition ? doSomething() : doSomethingElse();
Object result;
if (condition) {
result = doSomething();
} else {
result = doSomethingElse();
}
Favor expressions when
possible, but in most cases this is
not a choice and it is determined
by the language’s syntax
69. Iteration vs Recursion
int sumAll(int n) {
int result = 0;
for (int i = 0; i <= n; i++) {
result += i;
}
return result;
}
int sumAll(int n) {
return IntStream.rangeClosed(0, n).sum();
}
70. Iteration vs Recursion
int sumAll(int n) {
int result = 0;
for (int i = 0; i <= n; i++) {
result += i;
}
return result;
}
int sumAll(int n) {
return IntStream.rangeClosed(0, n).sum();
}
int sumAll(int n) {
return n == 0 ? 0 : n + sumAll(n - 1);
}
71. FP in the small, OOP in the large ...
FP for calculation parts
OOP for stateful and architectural parts
72. FP in the small, OOP in the large ...
FP for calculation parts
OOP for stateful and architectural parts
FP to orchestrate and
combine small components
(objects) in a functional way
... or viceversa?
73. 85% functional language purity
My real position is this: 100% pure
functional programing doesn’t work. Even
98% pure functional programming doesn’t
work. But if the slider between functional
purity and 1980s BASIC-style imperative
messiness is kicked down a few notches —
say to 85% — then it really does work. You
get all the advantages of functional
programming, but without the extreme mental
effort and unmaintainability that increases as
you get closer and closer to perfectly pure.
- James Hague
74. 85% functional language purity
My real position is this: 100% pure
functional programing doesn’t work. Even
98% pure functional programming doesn’t
work. But if the slider between functional
purity and 1980s BASIC-style imperative
messiness is kicked down a few notches —
say to 85% — then it really does work. You
get all the advantages of functional
programming, but without the extreme mental
effort and unmaintainability that increases as
you get closer and closer to perfectly pure.
- James Hague Perfect is the enemy of good
Voltaire
76. ● OOP approach models the data (noun-based) while FP
processes them (action/verb-based)
● Good software is written in both styles, because it has
more than one need to satisfy
● Programming languages are becoming evolved enough so
that any attempt of categorization is fuzzy and unpractical
● Clearly delineate which part of your code are purely
functional (e.g. because they need to run in parallel) and
which are not (for efficiency or readability)
● Poly-paradigm programming is more generic, powerful
and effective than polyglot programming
● From object-oriented to functional programming
to functions-first programming
● If you come from an OOP, study FP.
If you come from an FP, study OOP.
Key Takeaways
BE PRAGMATIC
77. Mario Fusco
Red Hat – Principal Software Engineer
mario.fusco@gmail.com
twitter: @mariofusco
Q A
Thanks ... Questions?