# How do I convert this code from using two separate types into code which uses a DU?

**URL:** <https://forums.fsharp.org/t/how-do-i-convert-this-code-from-using-two-separate-types-into-code-which-uses-a-du/2234>\
**Category:** Uncategorized\
**Tags:** beginners\
**Created:** [May 3, 2022, 12:53pm UTC](https://forums.fsharp.org/t/how-do-i-convert-this-code-from-using-two-separate-types-into-code-which-uses-a-du/2234 "2022-05-03T12:53:56Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [May 3, 2022, 12:53pm UTC](https://forums.fsharp.org/t/how-do-i-convert-this-code-from-using-two-separate-types-into-code-which-uses-a-du/2234/1 "2022-05-03T12:53:56Z")

</div>

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`.

```auto
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):

```auto
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.

---

<div class="post-metadata">

**Author:** ![ntwilson](https://yyz2.discourse-cdn.com/flex030/user_avatar/forums.fsharp.org/ntwilson/32/984_2.png) [@ntwilson](https://forums.fsharp.org/u/ntwilson)\
**Post date:** [May 3, 2022, 7:40pm UTC](https://forums.fsharp.org/t/how-do-i-convert-this-code-from-using-two-separate-types-into-code-which-uses-a-du/2234/2 "2022-05-03T19:40:06Z")

</div>

Short answer: I would start with your original code, and then just add an additional type for the union:

```auto
type Account = 
  | Personal of PersonalAccount
  | Joint of JointAccount

```

and then you can lump all accounts together (as `Account`s) but still have operations that are only valid on `PersonalAccount`s. Use the `Personal` or `Joint` functions to turn a `PersonalAccount` or `JointAccount` into an `Account`, and use pattern matching if you have an `Account` and want to get back to a `PersonalAccount` or `JointAccount`. e.g., if you have a pair of `Account`s, you’d have to pattern match in order to call the `makeJointWith` method:

```auto
let ans = 
  match acct1, acct2 with
  | Personal p1, Personal p2 -> Ok <| p1.makeJointWith p2
  | Joint acct, _ 
  | _, Joint acct -> Error <| sprintf "%s's account is already a joint account and cannot be combined" acct.Primary

```

or alternatively, throw an Exception if that’s how you’re handling error cases.  
You could take a list of accounts and extract the personal accounts:

```auto
let accounts : Account list = ...
let personalAccounts : PersonalAccount list = 
  accounts |> List.choose (fun acct -> match acct with | Personal p -> Some p | _ -> None)
// or shorthand
let personalAccounts : PersonalAccount list = 
  accounts |> List.choose (function | Personal p -> Some p | _ -> None)

```

Longer answer:  
In F#, a _type_ can be thought of as a _set_ of possible _values_ that something could take on. Sometimes that set is finite (the `bool` set only contains 2 values: `true` and `false`), sometimes that set is infinite or practically so (the `string` set contains every possible string, which certainly is not enumerable). When you say

```auto
type Account = 
  | Personal
  | Joint

```

(I’m leaving off the associated values for now), I’m creating 3 things:  
1 _type_ named `Account`, and  
2 _values_ named `Personal` and `Joint`.

The `Account` type can be thought of as a set of possible values - in this case 2 values, `Personal` and `Joint`. (At this point, I could rename `Account`/`Personal`/`Joint` to `bool`/`true`/`false`, and I’ve just reinvented the boolean type).

Types and values are very much separate - in any context you can only refer to one or the other. That’s why you’re getting an error saying `Personal` is not defined. You’re in a context that you can only talk about _types_ and there is no _type_ named `Personal` that’s defined. Only a `Personal` _value_. It would be like trying to include `true` in a type signature.

Whenever you want to define some operation that is only valid on some subset of values (like `makeJointAccount` which is only valid on 2 _personal_ accounts), you need to define a _type_ for the possible values that are allowed - so some sort of `type PersonalAccount` will need to exist.

Hope that helps!

---

<div class="post-metadata">

**Author:** ![GarryP](https://avatars.discourse-cdn.com/v4/letter/g/da6949/32.png) [@GarryP](https://forums.fsharp.org/u/GarryP)\
**Post date:** [May 4, 2022, 12:14pm UTC](https://forums.fsharp.org/t/how-do-i-convert-this-code-from-using-two-separate-types-into-code-which-uses-a-du/2234/3 "2022-05-04T12:14:16Z")

</div>

Thanks very much for the explanation.  
There are some things I haven’t looked at yet, e.g. backwards pipe, Result, and the ‘function’ keyword, but there’s lots for me try.  
Your explanation of types, in relation to values, is particularly useful and may help me to unlock some little mysteries that I haven’t managed to get my head properly around so far.  
Thanks again.
