Background here is I have a personal Elm app for running finance/budget simulations.
I had an Accounts
type, and everything was built on scenarios which had a number of Steps
type alias Step = Constants -> Date -> Accounts -> Accounts
That is, a Step
can look at the date, constants, current state of accounts, and apply a transfer
function to create an updated Accounts
.
All good, but I had no audit trail of the sequence of transfers that occurred. I didn’t want to burden the step implementations with logging, and I wanted to prevent composing Accounts -> Accounts
. So I un-exposed Account.transfer
and I created an explicit Transfer
type, and required Step
to be
type alias Step =
Constants -> Date -> Accounts -> List Transfer
Now a step could never be tempted to compose other steps and destroy the information about the sequence of transfers.
But it was so nice to have the old Step
type be composable:
compose : List Step -> Step
compose steps =
\constants date ->
steps
|> List.map (\step_ -> step_ constants date)
|> List.foldl (>>) identity
but that was no longer true.
Then here comes the epiphany that came as a bit of a surprise, and seemed a bit “dirty” and wrong, but…
I can compose multiple Accounts -> List Transfer
.
type alias Update =
Accounts -> List Transfer
andThen : Update -> Update -> Update
andThen nextUpdate prevUpdate =
\accounts ->
let
prevTransfers : List Transfer
prevTransfers =
prevUpdate accounts
intermediateAccountsValue : Accounts
intermediateAccountsValue =
prevTransfers |> List.foldl applyTransfer accounts
nextTransfers : List Transfer
nextTransfers =
nextUpdate intermediateAccountsValue
in
prevTransfers ++ nextTransfers
So now I can compose updates to accounts, but never lose the sequence of transfers that occurred.
The question for the more knowledgeable, is What is this pattern called"? This general idea of representing operations that are conditional on input (input -> operations)
, and composing them so as to maintain an audit trail of all operations that occurred?
(Note: I realize I’m applying transfers repeatedly in composing, and I’ll try to fix that later.)