如何在Haskell中修改状态的各个部分

时间:2015-10-13 05:50:05

标签: haskell higher-order-functions

我有许多修改System的操作。系统定义如下:

data System = Sys {
            sysId   :: Int,
            sysRand :: StdGen,
            sysProcesses :: ProcessDb,
            sysItems :: ItemDb
}  

与例如

type ProcessDb = M.Map Int Process

但是我也有一些函数,它们不需要访问完整的系统,但有类似的类型:

foo' :: (Process, ItemDb) -> ((Process, ItemDb),[Event])

目前我给了他们类似

的类型
foo: System -> (System, [Event])

但这是一个不必要的广泛接口。要将上面的窄界面与System结合使用,我必须从Process中提取单个ItemDbSystem,运行foo'然后修改{ {1}}结果。

这是一个非常包装和包装,并且导致更多的代码行,而不仅仅是传递系统,让System提取它需要的任何内容。在后一种情况下,包装和展开与实际的foo操作混合在一起,我觉得这两个方面应该分开。

我想我需要某种提升操作,将狭窄的foo'变成foo'。我想我可以写这个,但是我必须为窄函数的每个签名写一个这样的升降器,结果是很多不同的升降器。

  • 有没有成语如何解决这些问题?
  • 值得打扰吗?

2 个答案:

答案 0 :(得分:3)

一个常见的解决方案是使用一个类,可能由Control.Lens.TH.makeClassy的模板Haskell魔法创建。要点是你传递了整个System,但你不要让函数知道那就是你给它的东西。所有它被允许知道的是,你给它的东西提供了获取和/或修改它应该处理的部分的方法。

答案 1 :(得分:0)

我最终编写了一个可以在任何国家工作的功能,并且需要一个"镜头"它捕捉了从较大的国家到较小的国家的特定转变,然后回归

public

它允许我写像

这样的东西
focus :: (Lens s' s) -> State s' a -> State s a
focus lens ms'= do
    s <- get
    let (s', set) = lens s
        (a, s'')  = runState ms' s'
    put (set s'')
    return a

哪一步操作&#34;较小&#34;状态

run :: ExitP -> State SimState Log
...
   do
       evqs' <-focus onSys $ step (t,evt)
       ...

这里onSys是一个&#34;镜头&#34;它的工作原理如下:

step :: Timed Event -> State Sys.System [EventQu]

,其中

onSys :: Lens Sys.System SimState 
onSys (Sis e s) = (s, Sis e)

我认为现有的镜头库遵循类似的方法,但更多的魔法,比如自动创建镜头。我确实回避了镜头。相反,我很高兴地意识到只需几行代码即可获得我需要的东西。