Archive

Monthly Archives: December 2016

In previous posts, I have discussed my implementation details for the Bank Account kata.

This post will provide details on how I property-based test one of my functions. Specifically, I show the source code for testing a deposit.

Here are the types:

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Commands =
    | Withdraw of Account * decimal
    | Deposit  of Account * decimal
    | Transfer of Account * Account * decimal

type Response =
    | Withdrawal       of State * State
    | WithdrawalFailed of Account * decimal

    | Deposited        of State * State             
    | DepositFailed    of Account * decimal

    | Transferred      of TransferSummary   * decimal
    | TransferFailed   of Account * Account * decimal

and State =
    | BeforeDeposit of Account * decimal
    | AfterDeposit  of Account

    | BeforeWithdrawal of Account * decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

The function is below:

let handleDeposit = function
    | Deposit (account , amount) when amount > 0m && amount <= 1000000000m ->
           Some (Deposited((BeforeDeposit (account , amount),
                            AfterDeposit  (credit account amount) )))
    | Deposit (account , amount) ->
           Some (DepositFailed (account , amount))
    | _ -> None

The following code is a property-based test for the function above:

(*Tests*)
open FsCheck
open FsCheck.Xunit

type Run100KAttribute() =
    inherit PropertyAttribute(
        MaxTest = 100000,
        QuietOnSuccess = true)

[<Run100K>]
let ``deposits greater than zero AND less than 1 million always succeed`` () =

    // Setup
    let validDeposits = function
        | Deposit (acct , bal) -> bal > 0m && 
                                  bal <= 1000000m
        | _ -> false

    let isDeposited deposit = function
        | Some v -> match v with
                    | Deposited _ -> true
                    | _           -> false
        | _ -> false

    // Test
    Arb.generate<Commands> 
    |> Gen.filter validDeposits
    |> Arb.fromGen
    |> Prop.forAll 
    <| fun deposit -> handleDeposit deposit 
                      |> isDeposited

In the Bank Account Kata (vol. 1 & 2) video, I expressed disappointment on how I implemented my handlers.

The handlers were the following:

let handleWithdraw =  function
    | Withdraw (account , amount) ->
      if canWithdraw account amount then
           Some (Withdrawal(BeforeWithdrawal (account ,     amount),
                            AfterWithdrawal  (debit account amount)))
      else Some (WithdrawalFailed (account, amount) )
    | _ -> None

let handleDeposit =   function
    | Deposit   (account , amount) ->
      if amount > 0m then
           Some (Deposited((BeforeDeposit (account , amount),
                            AfterDeposit  (credit account amount) )))
      else Some (DepositFailed (account , amount))
    | _ -> None

let handleTransfer =  function
    | Transfer (fromAccount, toAccount, amount) ->
      if canWithdraw fromAccount amount then
          Some (Transferred ({ FromBalanceBefore=fromAccount
                               ToBalanceBefore=  toAccount

                               FromBalanceAfter= debit  fromAccount amount
                               ToBalanceAfter=   credit toAccount   amount } ,

                             amount))
          else Some (TransferFailed (fromAccount, toAccount, amount))
    | _ -> None

Let’s look at the first function:

let handleWithdraw =  function
    | Withdraw (account , amount) ->
      if canWithdraw account amount then
           Some (Withdrawal(BeforeWithdrawal (account ,     amount),
                            AfterWithdrawal  (debit account amount)))
      else Some (WithdrawalFailed (account, amount) )
    | _ -> None

The function above did not read as well as I would like. As a result I refactored the function to fully embrace pattern matching:

let handleWithdraw =  function
    | Withdraw (account , amount) when canWithdraw account amount ->
           Some (Withdrawal(BeforeWithdrawal (account ,     amount),
                            AfterWithdrawal  (debit account amount)))
    | Withdraw (account , amount) when not (canWithdraw account amount) ->
           Some (WithdrawalFailed (account, amount) )
    | _ -> None

Here’s the refactoring of all three functions:

let handleWithdraw =  function
    | Withdraw (account , amount) when canWithdraw account amount ->
           Some (Withdrawal(BeforeWithdrawal (account ,     amount),
                            AfterWithdrawal  (debit account amount)))
    | Withdraw (account , amount) when not (canWithdraw account amount) ->
           Some (WithdrawalFailed (account, amount) )
    | _ -> None

let handleDeposit =   function
    | Deposit (account , amount) when amount > 0m ->
            Some (Deposited((BeforeDeposit (account , amount),
                             AfterDeposit  (credit account amount) )))
    | Deposit (account , amount) when amount <= 0m ->
            Some (DepositFailed (account , amount))
    | _ -> None

let handleTransfer =  function
    | Transfer (fromAccount, toAccount, amount) when canWithdraw fromAccount amount ->
           Some (Transferred ({ FromBalanceBefore=fromAccount
                                ToBalanceBefore=  toAccount
                                FromBalanceAfter= debit  fromAccount amount
                                ToBalanceAfter=   credit toAccount   amount } ,
                                amount))

    | Transfer (fromAccount, toAccount, amount) when not (canWithdraw fromAccount amount) ->
           Some (TransferFailed (fromAccount, toAccount, amount))
    | _ -> None

Angry Developer: I’m having a developer identity crisis trying to figure out what my niche should be.  Seems like there’s always web work, but no two shops are using more than a few of the same tools and frameworks.  I’d really like to avoid becoming an expert at some doomed technology, if that can be helped.

Scott Nimrod: I think Angular is a safe bet for the next 6 months.
😉

Angry Developer: You mean Angular2 –Angular is obsolete shit that must be replaced now.
Scott Nimrod: Yea. That.

Angry Developer: That’s the funnest part of JavaScript dev.  By the time you’re an expert at a stack, it’s changed completely.

Angry Developer: I completely wasted about 5 hours last week learning, trying, and failing with code examples and tutorials on Ionic and Angular because (once I looked more carefully) I was looking a examples for the previous frameworks (which were current a few month ago).

Angry Developer: Doh!

Angry Developer: I much prefer slower, more thoughtful languages and frameworks.  I haven’t had to throw away more than a few pages of what I knew about .NET 1.1

Angry Developer: The JS hackers are changing their frameworks constantly because they didn’t think things through very carefully in the initial design.

In the previous Bank Account kata, I attempted to implement happy path logic using a function-driven approach to later discover my types that would complement them.

Note that my initial implementation did not cover edge case scenarios.

For example:
Can one withdraw an amount that exceeds their balance?

Can one deposit absolutely nothing into their account?

My types were updated to the following:

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Commands =
    | Withdraw of Account * decimal
    | Deposit  of Account * decimal
    | Transfer of Account * Account * decimal

type Response =
    | Withdrawal       of State * State
    | WithdrawalFailed of Account * decimal

    | Deposited        of State * State             
    | DepositFailed    of Account * decimal

    | Transferred      of TransferSummary   * decimal
    | TransferFailed   of Account * Account * decimal

and State =
    | BeforeDeposit of Account * decimal
    | AfterDeposit  of Account

    | BeforeWithdrawal of Account * decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

My functions were updated to the following:

(*Functions*)
let balanceOf = function
                | Checking v
                | Savings  v
                | Business v -> v

let update account amount operator = account |>  function
           | Checking v -> Checking (v + (operator * amount))
           | Savings  v -> Savings  (v + (operator * amount))
           | Business v -> Business (v + (operator * amount))

let debit       account amount =  update account amount (- 1m)
let credit      account amount =  update account amount (+ 1m)
let canWithdraw account amount = balanceOf account >= amount

let handleWithdraw =  function
                      | Withdraw (account , amount) -> 
                        if canWithdraw account amount then
                             Some (Withdrawal(BeforeWithdrawal (account ,     amount),
                                              AfterWithdrawal  (debit account amount)))
                        else Some (WithdrawalFailed (account, amount) )
                      | _ -> None

let handleDeposit =   function
                      | Deposit   (account , amount) ->
                        if amount > 0m then 
                             Some (Deposited((BeforeDeposit (account , amount),
                                              AfterDeposit  (credit account amount) )))
                        else Some (DepositFailed (account , amount))
                      | _ -> None

let handleTransfer =  function
                      | Transfer (fromAccount, toAccount, amount) ->
                        if canWithdraw fromAccount amount then
                            Some (Transferred ({ FromBalanceBefore=fromAccount
                                                 ToBalanceBefore=  toAccount

                                                 FromBalanceAfter= debit  fromAccount amount 
                                                 ToBalanceAfter=   credit toAccount   amount } ,

                                               amount))
                            else Some (TransferFailed (fromAccount, toAccount, amount))
                      | _ -> None

Here are some client calls for the updated code:

(*Client*)
let deposit =   handleDeposit  (Deposit  (Checking 100m , 10m))
let withdrawl = handleWithdraw (Withdraw (Savings  10m , 10m))
let transfer =  handleTransfer (Transfer (Checking 100m , Savings 150m , 10m))

Here’s the output from the client calls:

val deposit : Response option =
  Some
    (Deposited
       (BeforeDeposit (Checking 100M,10M),AfterDeposit (Checking 110M)))
val withdrawl : Response option =
  Some
    (Withdrawal
       (BeforeWithdrawal (Savings 10M,10M),AfterWithdrawal (Savings 0M)))
val transfer : Response option =
  Some (Transferred ({FromBalanceBefore = Checking 100M;
                      ToBalanceBefore = Savings 150M;
                      FromBalanceAfter = Checking 90M;
                      ToBalanceAfter = Savings 160M;},10M))

The entire domain is below:

module BankAccountImpl2

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Commands =
    | Withdraw of Account * decimal
    | Deposit  of Account * decimal
    | Transfer of Account * Account * decimal

type Response =
    | Withdrawal       of State * State
    | WithdrawalFailed of Account * decimal

    | Deposited        of State * State             
    | DepositFailed    of Account * decimal

    | Transferred      of TransferSummary   * decimal
    | TransferFailed   of Account * Account * decimal

and State =
    | BeforeDeposit of Account * decimal
    | AfterDeposit  of Account

    | BeforeWithdrawal of Account * decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

(*Functions*)
let balanceOf = function
                | Checking v
                | Savings  v
                | Business v -> v

let update account amount operator = account |>  function
           | Checking v -> Checking (v + (operator * amount))
           | Savings  v -> Savings  (v + (operator * amount))
           | Business v -> Business (v + (operator * amount))

let debit       account amount =  update account amount (- 1m)
let credit      account amount =  update account amount (+ 1m)
let canWithdraw account amount = balanceOf account >= amount

let handleWithdraw =  function
                      | Withdraw (account , amount) -> 
                        if canWithdraw account amount then
                             Some (Withdrawal(BeforeWithdrawal (account ,     amount),
                                              AfterWithdrawal  (debit account amount)))
                        else Some (WithdrawalFailed (account, amount) )
                      | _ -> None

let handleDeposit =   function
                      | Deposit   (account , amount) ->
                        if amount > 0m then 
                             Some (Deposited((BeforeDeposit (account , amount),
                                              AfterDeposit  (credit account amount) )))
                        else Some (DepositFailed (account , amount))
                      | _ -> None

let handleTransfer =  function
                      | Transfer (fromAccount, toAccount, amount) ->
                        if canWithdraw fromAccount amount then
                            Some (Transferred ({ FromBalanceBefore=fromAccount
                                                 ToBalanceBefore=  toAccount

                                                 FromBalanceAfter= debit  fromAccount amount 
                                                 ToBalanceAfter=   credit toAccount   amount } ,

                                               amount))
                            else Some (TransferFailed (fromAccount, toAccount, amount))
                      | _ -> None

(*Client*)
let deposit =   handleDeposit  (Deposit  (Checking 100m , 10m))
let withdrawl = handleWithdraw (Withdraw (Savings  10m , 10m))
let transfer =  handleTransfer (Transfer (Checking 100m , Savings 150m , 10m))

I wrote some F# code for handling bank account transactions.

I wrote the program by using a function driven approach instead of a type driven approach.

The main handlers are the following:

let handleWithdraw = function
                     | Withdraw   (account , amount) -> 
                            Some  (BeforeWithdrawal (account , -1m * amount),
                                   AfterWithdrawal  (debit account amount) )
                     | _ -> None

let handleDeposit =   function
                      | Deposit   (account , amount) -> 
                             Some (BeforeDeposit (account , 1m * amount),
                                   AfterDeposit  (credit account amount) )
                      | _ -> None

let handleTransfer =  function
                      | Transfer (fromAccount, toAccount, amount) ->
                        Some (Transferred ({ FromBalanceBefore=fromAccount
                                             ToBalanceBefore=  toAccount

                                             FromBalanceAfter= debit  fromAccount amount 
                                             ToBalanceAfter=   credit toAccount   amount } ,

                                           amount))
                      | _ -> None

The supporting functions that back the handlers are as follows:

let update account amount operator = account |>  function
           | Checking v -> Checking (v + (operator * amount))
           | Savings  v -> Savings  (v + (operator * amount))
           | Business v -> Business (v + (operator * amount))

let debit  account amount =  update account amount (- 1m)
let credit account amount =  update account amount (+ 1m)

Here are the types:

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Commands =
    | Withdraw of Account * decimal
    | Deposit  of Account * decimal
    | Transfer of Account * Account * decimal

type Response =
    | Withdrawal  of Account
    | Deposited   of Account
    | Transferred of TransferSummary * decimal

and Transaction =
    | BeforeDeposit of Account * decimal
    | AfterDeposit  of Account

    | BeforeWithdrawal of Account * decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

The entire program is below:

module BankAccountImpl

type Account =        
    | Checking of decimal
    | Savings  of decimal
    | Business of decimal

type Commands =
    | Withdrawal of Account * decimal
    | Deposit   of Account * decimal
    | Transfer  of Account * Account * decimal

type Response =
    | Withdrawal  of Account
    | Deposited   of Account
    | Transferred of TransferSummary * decimal

and Transaction =
    | BeforeDeposit of Account * decimal
    | AfterDeposit  of Account

    | BeforeWithdrawal of Account * decimal
    | AfterWithdrawal  of Account

and TransferSummary = { 
    FromBalanceBefore: Account ; ToBalanceBefore: Account
    FromBalanceAfter:  Account ; ToBalanceAfter:  Account }

(*Functions*)
let update account amount operator = account |>  function
           | Checking v -> Checking (v + (operator * amount))
           | Savings  v -> Savings  (v + (operator * amount))
           | Business v -> Business (v + (operator * amount))

let debit  account amount =  update account amount (- 1m)
let credit account amount =  update account amount (+ 1m)

let handleWithdraw = function
                     | Withdraw  (account , amount) -> 
                            Some (BeforeWithdrawal (account , -1m * amount),
                                  AfterWithdrawal  (debit account amount) )
                     | _ -> None

let handleDeposit =   function
                      | Deposit   (account , amount) -> 
                             Some (BeforeDeposit (account , 1m * amount),
                                   AfterDeposit  (credit account amount) )
                      | _ -> None

let handleTransfer =  function
                      | Transfer (fromAccount, toAccount, amount) ->
                        Some (Transferred ({ FromBalanceBefore=fromAccount
                                             ToBalanceBefore=  toAccount

                                             FromBalanceAfter= debit  fromAccount amount 
                                             ToBalanceAfter=   credit toAccount   amount } ,

                                           amount))
                      | _ -> None

(*Client*)
let deposit =    handleDeposit   (Deposit  (Checking 100m , 10m))
let withdraw =   handleWithdraw  (Withdrawal (Savings 100m , 10m))
let transfer =   handleTransfer  (Transfer (Checking 100m , Savings 150m , 10m))