What would define FP in java? I usually hear it thrown around when talking about streams/ functional interfaces
Let me write out some examples let me write out some examples
public final class BankAccount {
private int balance;
public BankAccount() {
this.balance = 0;
}
public void deposit(int amt) {
if (amt < 0) {
throw new IllegalArgumentException("amt must be non-negative");
}
else {
this.balance += amt;
}
}
public int withdraw(int amt) {
if (amt < 0) {
throw new IllegalArgumentException("amt must be non-negative");
}
else {
if (this.balance < amt) {
int balance = this.balance;
this.balance = 0;
return balance;
}
else {
this.balance -= amt;
return amt;
}
}
}
public int getBalance() {
return this.balance;
}
}
Okay real basic class here i just whipped up. Take a moment to read it and show me what an example usage might be.
(this isn't FP, this is normal)
(i'm also sure there is a bug, but ignore it)
So an example like:
= new BankAccount(); BankAccount myTaxHaven .deposit(500); myTaxHaven.withdraw(250); myTaxHaven
?
sure.
Mutable bank account - you can take money in and out.
Now here is the challenge. We have a new requirement. We want to know the state of every bank account at every time.
So somewhere in the code there is a map of user id to a map of timestamp to bank account. (does that parse?)
transaction timestamp?
yeah
yep I'm tracking
But in order to support that use case we can't change bank accounts directly like that.
Or rather, one way to support it is to make the bank account immutable.
Wait, why can't we change bank accounts directly?
lets say we had this
Map<Instant, BankAccount> bankAccountAtTime = new HashMap<>();
= new BankAccount();
BankAccount myTaxHaven .put(Instant.now(), myTaxHaven);
bankAccountAtTime.deposit(400); // oh no, our history is messed up myTaxHaven
Why is our history messed up?
so the fact that we mutate our bank account after depositing? That makes me question the original claim that this is a solution
I mean yeah we're mapping to the bank accounts, not a specific balance of the bank account in that time
My framing here is a bit messed up. I guess I'm saying if they were immutable then this kind of solution would work.
If they were immutable, deposit wouldn't even do the same thing though
Thats correct.
So how would we re-write the code such that we support all the same use cases but our contract doesn't have any mutating methods.
Keep a list of <Instant, Balance> or <Instant, Transaction>(which contains before/after information) Or a map, but some data structure
I mean just the BankAccount class.
deposit/withdraw could return a Balance or Transaction object, but then I think our paradigm doesn't even make sense
public final class BankAccount {
private final int balance;
public BankAccount() {
this(0);
}
private BankAccount(int balance) {
this.balance = balance;
}
public BankAccount deposit(int amt) {
if (amt < 0) {
throw new IllegalArgumentException("amt must be non-negative");
}
else {
return new BankAccount(this.balance - amt);
}
}
WithdrawalResult(BankAccount account, int withdrawn) {}
record
public WithdrawalResult withdraw(int amt) {
if (amt < 0) {
throw new IllegalArgumentException("amt must be non-negative");
}
else {
if (this.balance < amt) {
return new WithdrawalResult(new BankAccount(0), this.balance);
}
else {
return new WithdrawalResult(new BankAccount(this.balance - amt), amt);
}
}
}
public int getBalance() {
return this.balance;
}
}
A bank account is a full history - always. There is the current state of it and past states of it, but they are all equally real "accounts".
right, that's a fine way of viewing it yeah
It's like numbers. Imagine if this worked
Integer i = 5;
.subtractOne(); // i is now 4 i
the value of 5 should be independent from the identity you assign that value. If that isn't the case - for numbers - it feels super wierd
Is the main point that mutability is inherently dangerous?
Kinda. there are a lot of downsides to it - it's a lot harder to multithread is a big one.
And you can always get it back if you need to.
Yeah this makes sense I definitely believe in keeping an immutable history for anything important