More Related Content Similar to Pure Functions and Immutable Objects (20) More from Victor Rentea (16) Pure Functions and Immutable Objects3. Technical Training
HibernateSpring Func Prog in Java
300+ days2000 devs8 years
Details for you or your company: VictorRentea.ro
40 companies
Follow me:
35K 4K 3K
Java PerformanceReactive-X
Design Patterns
Clean Code
Refactoring
Unit Testing
TDD
any
lang
5. 163 © VictorRentea.ro
a training by
checkCustomer(customer);
checkOrder(customer, order);
Mock-full tests
Race Bugs
A method changes a parameter: Surprise!
Different Results for Same Inputs
customer.setActive(true);
Temporal Coupling
6. 164 © VictorRentea.ro
a training by
do side-effects
return void sendEmail(Email):void
Command-Query Separation
setActive(true):void
return resultssearch(criteria):List
computePrice(movie):int
in 1994, by Bertrand Meyer
Pure Functions
7. 165 © VictorRentea.ro
a training by
No side effects
No INSERTs, POSTs, queues, files, fields,…
= 𝑀𝑎𝑡ℎ𝑒𝑚𝑎𝑡𝑖𝑐𝑎𝑙 𝐹𝑢𝑛𝑐𝑡𝑖𝑜𝑛𝑠: 𝑓 𝑥, 𝑦 = 𝑥2
+ 𝑦
(logging doesn't count)
Referential Transparent
Same arguments ➔ same result
No current time, random, GET, SELECT…
≠ Idempotent
Pure Functions
8. 167 © VictorRentea.ro
a training by
Pure Functions : Quiz
f1(int x) {return x + 1;}
f2(Data d) {return ++d.x;}
f3() {d.incrementX(); return d.x;}
f4() {return querySQL(...);}
f5(int y) { return this.x + y; }
f6(Data d, int y) { return d.getX() + y; }
f7(int i) { if (i<0) throw new WrongInputException(); }
is this immutable?
Probable side effects
Expected to be pure
9. 168 © VictorRentea.ro
a training by
throw new E(); is pure
f(x) {
try {
//
}
}
catch (E) is pure?
if it always throws for the same inputs
it depends ...
* Some slightly disagree
on E
NO, if E can happen randomly
eg. IOException, OutOfMemory
YES, if E is thrown deterministically*
➔ Catch unexpected exceptions
in the outskirts of your code
10. 169 © VictorRentea.ro
a training by
Why we Love Pure Functions
➢No hidden inputs, only plain-sight return values and parameters
➢Easier to understand
➢Testable (less setup)
➢Fast & Composable: free to call them n times
➢No temporal coupling
➢Parallelizable
11. 170 © VictorRentea.ro
a training by
That's it!
I'll only write pure functions from now!
impossible
What kind of app doesn't change anything?
12. 172 © VictorRentea.ro
a training by
In Java there's no way to strictly enforce purity
So we'll live with both pure and impure functions
But how do we distinguish them?
13. 173 © VictorRentea.ro
a training by
do side-effects
return void sendEmail(Email):void
Command-Query Separation
setActive(true):void
return results
pure functions
search():List
computePrice(movie):int
Highlight Side Effects
computePriceAndAdjustMetrics(movie):int
16. 179 © VictorRentea.ro
a training by
functional core
Side-effects (Writes) +
Non-deterministic Reads
Expose them
Purify the most complex parts of your logic!
18. 182 © VictorRentea.ro
a training by
Purifying Logic
Time and Random
Amount of time-dependent logic:
➢None (e.setCreationDate(now());) ➔ tolerate
➢Very little ➔ Inject a Clock / TimeProvider
➢Heavy (x-rare) ➔ expose a ..., time); parameter
20. 185 © VictorRentea.ro
a training by
Purifying Logic
Expose Read/Write from DB/APIs
Initial Read
Intermediary
(conditional?)
➔ Pass as Parameters
➔ Split-Phase Refactor f();
r=read();
f(r);
Writing Results ➔ Return Changes w=f();
persist(w);
r=read()
21. 186 © VictorRentea.ro
a training by
Purifying Logic
Expose Read/Write from DB/APIs
Initial Read
Intermediary
(conditional?)
➔ Pass as Parameters
➔ Split-Phase Refactor
r=read();
f(r);
r1=f1()
f2(r,r1...)
expose impurity
Writing Results ➔ Return Changes w=f();
persist(w);
r=read()
24. 190 © VictorRentea.ro
a training by
void f(Data data) {
...
if (data.getX() == 1) {
// will this run ?
}
}
void h() {
Data data = new Data(1);
obj.setData(data);
g(data);
}
obj
void g(Data data) {
data.setX(2);
mutateParam(data);
obj.mutateField();
f(data);
}
void setData(Data data) {
this.data = data;
}
void mutateField() {
this.data.setX(2);
}
same obj
in h() and g()
void mutateParam(Data data) {
data.setX(1);
}
x=
Long-lived mutable data
A Code Inspection Session
What code ran before,
having a reference
to my data instance?!
Mutable Data
25. 191 © VictorRentea.ro
a training by
void f(Data data) {
...
if (data.getX() == 1) {
// will this run ?
}
}
void h() {
Data data = new Data(1);
obj.setData(data);
g(data);
}
obj
void g(Data data) {
data.setX(2);
mutateParam(data);
obj.mutateField();
f(data);
}
void setData(Data data) {
this.data = data;
}
void mutateField() {
this.data.setX(2);
}
same obj
in h() and g()
void mutateParam(Data data) {
data.setX(1);
}
x=
Long-lived mutable data
A Code Inspection Session
What code ran before,
having a reference
to my data instance?!
Mutable DataImmutable Data
26. 192 © VictorRentea.ro
a training by
void f(Data data) {
...
if (data.getX() == 1) {
// will this run ?
}
}
void g(Data data) {
f(data);
}
void h() {
Data data = new Data(1);
g(data);
}
A Code Inspection Session
Immutable Data
Who created
the instance?!
Easier to trace
data changes
Real-world: 4+ functions in between
...
Real-world: 4+
...
x=
27. 193 © VictorRentea.ro
a training by
Designing Immutable Classes
public class A {
private final String s;
private final B b;
private final List<String> list;
public A(String s, B b, List<String> list) {
this.s = s;
this.b = b;
this.list = new ArrayList<>(list);
// validation ...
}
public List<String> getList() {
return unmodifiableList(list);
}
// other getters
// hashCode, equals on all fields = Value Object
// bits of LOGIC 💪
public A withS(String newS) {
return new A(newS, b, list);
}
}
Mutates by creating
a new instance
Stops creator keeping a reference
Overkill, as problem is not the creator but the "man-in-the-middle"
Oh, so
we CAN
mutate them!
@lombok.With
Iterable<String> getList() {
List<? extends String> getList() {or
record
(java 15)
Java collections are mutable😞
final
Afraid of hackers? 😨
@lombok.Value
or, until then...
29. 195 © VictorRentea.ro
a training by
A function changing the object has to return it:
data = f(data);
Imagine data has 20 fields
When did data.x change?
... every time
data = g(data);
data = h(data);
The mess is still here!
30. 196 © VictorRentea.ro
a training by
data = f(data);
final variables won't allow this
IntelliJ underlines
reassigned variables ❤️
By the way, there are ways to add final automatically at code cleanup
Real Problem
Too Large Immutable Objects
smallerData = f(...);
➔ break them
If they change together,
they stick together
data = g(data);
data = h(data);
withX
withY
withZ
32. 198 © VictorRentea.ro
a training by
void f(VO[] arr) {
arr[0] = arr[0].withX(-99);
}
void f(List<String> list) {
list.removeIf(String::isBlank);
}
void f(Map<Integer, VO> map) {
map.put(1, map.get(1).withX(-99));
}
map.get(1).withX(-99)
34. 200 © VictorRentea.ro
a training by
Why we Immutable objects
Easier to trace data changes
Can enforce validation in constructor
Safe to put in Set or Map(as keys)
Thread-safe ➔ no race bugs, since they can't be changed
35. 201 © VictorRentea.ro
a training by
All right cowboy!
Only immutable objects from now on!
usually that's too much!
36. 202 © VictorRentea.ro
a training by
Value Object vs Reference Object
class Customer {
id // PK in DB
name
phone
}
class Money {
amount
currency
}
equals(): using what fields ?
idamount, currency
final
final
Make VOs immutable!
Should Entities
be immutable?
No matter how different two instances are,
if they have the same id, it's the same Customer
37. 203 © VictorRentea.ro
a training by
instead,
Extract immutable Value Objects from them
Leaving the root Entity mutable
NO*
*there are few cases when it pays to, but those apps typically don't persist their data
Should Entities be immutable?
38. 204 © VictorRentea.ro
a training by
Entity
(mutable)
Persistent Leaf
eg. FullName
Immutable Objects in Real Life
Runtime Objects
(non-persistent data)
continuously
break XL entities
Hibernate: @Embeddable
40. 207 © VictorRentea.ro
a training by
The Big Deal
Don't mutate objects on long workflows!
a(e) b(x) c(x) d(x) e(x) f(x) g(e) {
e.setField(...);
}
a(e) {
String s = b(vo);
e.setField(s);
}
b(…) c(…) d(…) e(…) f(…) g(…) {return ...;}
1) vavr.Tuple3<String,String,Integer>
2) NewConceptVO #kudos if you can find a good name!
can be pure functions
Immutable Arguments
Return the change to the surface, and apply it there
How to return changes
to multiple fields:
44. 212 © VictorRentea.ro
a training by
Avoid Immutable Objects If
- Trashing millions of instances/second
- Cloning Lots of Lists
- Trivial logic
- Persistent Entities
45. 213 © VictorRentea.ro
a training by
Take-Aways
➢ Complex logic ➔ pure functions using immutable objects
➢ Functional Core / Imperative Shell
➢ Pull impure remote/DB calls in the shell
➢ We'll change it in there ➔ compute and return
➢ Without proper mindset, immutability can hurt
➢ Don't mutate: argument state, variables or collections
➢ Immutable: runtime data or persistent leaves
➢ We'll change it in there ➔ compute and return
And no, I'm against OOP; but not in huge logic code ➔
46. 214 © VictorRentea.ro
a training by
victorrentea@gmail.com ♦ ♦ Training: VictorRentea.ro
➢We'll change it in there ➔ compute and return