使用动态函数更新多个集合的元素

Joh*_*ler 4 haskell data-modeling ioref

设置:

我有几个各种数据结构的集合,代表虚拟系统中模拟对象的状态.我还有许多函数可以转换(即基于原始对象和0或更多参数创建对象的新副本)这些对象.

目标是允许用户选择一些对象来应用转换(在模拟规则内),将这些函数应用于这些对象,并通过用新的对象替换旧对象来更新集合.

我希望能够通过将较小的变换组合成较大的变换来构建这种类型的函数.然后评估这个组合功能.

问题:

如何构建我的程序以使其成为可能?

我用什么样的组合来建立像这样的交易?

想法:

  1. 将所有集合放入一个巨大的结构中并传递这个结构.
  2. 使用状态monad来完成基本相同的事情
  3. 使用IORef(或其中一个更强大的表兄弟,如MVar)并构建一个IO动作
  4. 使用功能反应式编程框架

1和2看起来像是带着很多行李,特别是如果我想最终将一些集合移动到数据库中.(Darn IO Monad)

3似乎运作良好但开始看起来很像重新创建OOP.我也不确定在什么级别使用IORef.(例如IORef (Collection Obj)Collection (IORef Obj)data Obj {field::IORef(Type)} )

4感觉功能最强大,但它似乎也创造了很多代码复杂性而在表现力方面没有太大的回报.


我有一个网店前面.我维护了一系列产品,其中包括库存数量和价格(等等).我还有一些对商店有信用的用户.

用户出现并选择3个产品购买并使用商店信用结账.我需要创建一个新产品集合,其中有3个产品的库存量减少,创建一个新的用户集合,用户帐户借记.

这意味着我得到以下内容:

checkout :: Cart -> ProductsCol -> UserCol -> (ProductsCol, UserCol)
Run Code Online (Sandbox Code Playgroud)

但是生活变得更复杂,我需要处理税收:

checkout :: Cart -> ProductsCol -> UserCol -> TaxCol 
            -> (ProductsCol, UserCol, TaxCol)
Run Code Online (Sandbox Code Playgroud)

然后我需要确保将订单添加到发货队列:

checkout :: Cart 
         -> ProductsCol 
         -> UserCol 
         -> TaxCol
         -> ShipList
         -> (ProductsCol, UserCol, TaxCol, ShipList)
Run Code Online (Sandbox Code Playgroud)

等等...

我想写的是类似的东西

checkout = updateStockAmount <*> applyUserCredit <*> payTaxes <*> shipProducts
applyUserCredit = debitUser <*> creditBalanceSheet
Run Code Online (Sandbox Code Playgroud)

但是类型检查员会对我产生中风.我如何构建这个商店,使这些checkoutapplyUserCredit功能保持模块化和抽象化?我不可能是唯一一个有这个问题的人,对吗?

C. *_*ann 6

好吧,让我们打破这个.

你有"更新"函数的类型,如A -> A各种特定类型A,可以从部分应用程序派生,根据以前的值指定某种类型的新值.每种类型A都应该特定于该函数的功能,并且随着程序的发展,应该很容易更改这些类型.

您还有某种共享状态,可能包含任何上述更新功能使用的所有信息.此外,应该可以更改状态所包含的内容,而不会显着影响除直接作用于其之外的任何功能.

此外,您希望能够抽象地组合更新功能,而不会影响上述功能.

我们可以推断出简单设计的一些必要特征:

  • 在完全共享状态和每个函数所需的细节之间将需要一个中间层,允许状态的各个部分被投射出来并独立于其余部分进行替换.

  • 根据定义,更新函数本身的类型与没有真正的共享结构不兼容,因此要构成它们,首先需要将每个函数与中间层部分组合在一起.这将为您提供对整个州的更新,然后可以以明显的方式组成.

  • 共享状态作为一个整体所需的唯一操作是与中间层接口,以及维护所做更改所需的任何操作.

这种分解允许每个整个层在很大程度上是模块化的; 特别是,可以定义类型类来描述必要的功能,允许交换任何相关的实例.

特别是,这基本上统一了你的想法2和3.这里有某种固有的monadic上下文,并且建议的类型类接口允许多种方法,例如:

  • 使共享状态成为记录类型,将其存储在Statemonad中,并使用镜头提供接口层.

  • 使共享状态成为包含STRef每个部分的记录类型,并将字段选择器与STmonad更新操作组合以提供接口层.

  • 使共享状态成为TChans 的集合,具有单独的线程以适当地读/写它们以与外部数据存储异步通信.

或任何数量的其他变化.