I started off with the code below, with which I can create two types of very simplistic bank accounts: PersonalAccount
and JointAccount
.
PersonalAccount
s are for one person, JointAccount
s are for two people to share.
When a JointAccount
is created, most of the balances in both accounts are moved to the joint account but each person is left with a small amount (residual) in the new version of their personal account.
Only PersonalAccount
s can be ‘joined’ into a JointAccount
.
let minimumResidual = 50.0m
let calculateResidual balance = if balance < minimumResidual then balance else minimumResidual
type BasicAccountDetails =
{ Balance: decimal }
type JointAccount =
{ Primary: string
Secondary: string
Balance: decimal }
type PersonalAccount =
{ Name: string
Balance: decimal }
member this.makeJointWith (addition: PersonalAccount) =
let thisResidual = calculateResidual this.Balance
let additionResidual = calculateResidual addition.Balance
( { Primary = this.Name
Secondary = addition.Name
Balance = (this.Balance - thisResidual) + (addition.Balance - additionResidual) } ,
{ this with Balance = thisResidual },
{ addition with Balance = additionResidual } )
let janesAccount = { Name = "Jane"; Balance = 200.0m }
let bobsAccount = { Name = "Bob"; Balance = 40.0m }
let jointAccount, janesNewAccount, bobsNewAccount = janesAccount.makeJointWith bobsAccount
That works fine but, since PersonalAccount
and JointAccount
are different types, they can’t be put into a collection together.
I then converted the code into what you can see next (don’t look if bad code makes you cry):
let maximumResidual = 50.0m
let calculateResidual balance = if balance < maximumResidual then balance else maximumResidual
type BasicDetails =
{ Balance: decimal }
type PersonalDetails =
{ Name: string }
type JointDetails =
{ Primary: string
Secondary: string }
type Account =
| Personal of BasicDetails * PersonalDetails
| Joint of BasicDetails * JointDetails
let janesAccount = Personal ({ Balance = 200.0m }, { Name = "Jane"})
let bobsAccount = Personal ({ Balance = 40.0m }, { Name = "Bob"})
let makeJointAccount (primary: Account) (secondary: Account) =
match primary with
| Personal (primaryBasic, primaryPersonal) ->
let primaryResidual = calculateResidual primaryBasic.Balance
match secondary with
| Personal (secondaryBasic, secondaryPersonal) ->
let secondaryResidual = calculateResidual secondaryBasic.Balance
( Joint ({Balance = (primaryBasic.Balance - primaryResidual) + (secondaryBasic.Balance - secondaryResidual)},
{ Primary = primaryPersonal.Name
Secondary = secondaryPersonal.Name } ),
Personal ({ primaryBasic with Balance = primaryResidual }, primaryPersonal ),
Personal ({ secondaryBasic with Balance = secondaryResidual }, secondaryPersonal) )
| _ -> primary, primary, primary
| _ -> primary, primary, primary
let jointAccount, janesNewaccount, bobsNewAccount = makeJointAccount janesAccount bobsAccount
As you can see, it’s horrible code which, while it returns the things I want when it runs under certain circumstances, it’s really not the right way to do things (returning the primary account three times when either account is not a Personal
account is clearly utterly wrong).
I just can’t seem to figure out how to make makeJointAccount only accept Personal
accounts; changing (primary: Account) to (primary: Personal) gives me an error that ‘Personal’ is not defined.
I’ve probably gone too far down the wrong route and can’t see my way back to where I need to be; the ‘fix’ is probably very simple.
Can anyone give me some suggestions for turning this pigs ear into a silk purse (or even a linen coin bag)?
The code is completely experimental so any changes can be done to it; nothing is necessary and any changes are welcome.