Customization
I have several collections of various data structures that represent the state of simulated objects in a virtual system. I also have a number of functions that convert (that is, create a new copy of an object based on the original and 0 or more parameters) these objects.
The goal is to allow the user to select an object to apply transformations (as part of the modeling rules), apply those functions to these objects, and update collections by replacing old objects with new ones.
I would like to be able to create a function of this type by combining smaller conversions into larger ones. Then evaluate this combined function.
Questions
How do I customize my program to make this possible?
What combinator do I use to create such a transaction?
Ideas
- Put all the collections in one huge structure and pass this structure around.
- Use the state monad to do basically the same thing.
- Use an IORef (or one of its more powerful cousins ββlike MVar) and create an IO action
- Using a functional reactive software platform
1 and 2, it looks like they carry a lot of luggage, especially if I assume that in the end some collections will be moved to the database. (Darn Io Monad)
3 seems to work well, but it starts to look a lot like recreating OOP. I'm also not sure at what level to use IORef. (for example, IORef (Collection Obj) or Collection (IORef Obj) or data Obj {field::IORef(Type)} )
4 feels most functional in style, but it also seems to create a lot of code complexity without significant impact in terms of expressiveness.
Example
I have a web store. I keep a collection of products with (among other things) stock quantity and price. I also have a collection of users who have credit in the store.
The user comes and selects 3 products for purchase and goes for registration using a loan in the store. I need to create a new collection of products that has stock in stock for 3 products, create a new collection of users with a debit user account.
This means that I get the following:
checkout :: Cart -> ProductsCol -> UserCol -> (ProductsCol, UserCol)
But then life gets complicated, and I need to deal with taxes:
checkout :: Cart -> ProductsCol -> UserCol -> TaxCol -> (ProductsCol, UserCol, TaxCol)
And then I must definitely add the order to the delivery queue:
checkout :: Cart -> ProductsCol -> UserCol -> TaxCol -> ShipList -> (ProductsCol, UserCol, TaxCol, ShipList)
And so on...
What I would like to write is something like
checkout = updateStockAmount <*> applyUserCredit <*> payTaxes <*> shipProducts applyUserCredit = debitUser <*> creditBalanceSheet
but the type-controller would be apoplectic on me. How to structure this storage so that the checkout or applyUserCredit remain modular and abstract? I can't be the only one to have this problem, right?