我有许多修改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
中提取单个ItemDb
和System
,运行foo'
然后修改{ {1}}结果。
这是一个非常包装和包装,并且导致更多的代码行,而不仅仅是传递系统,让System
提取它需要的任何内容。在后一种情况下,包装和展开与实际的foo
操作混合在一起,我觉得这两个方面应该分开。
我想我需要某种提升操作,将狭窄的foo'
变成foo'
。我想我可以写这个,但是我必须为窄函数的每个签名写一个这样的升降器,结果是很多不同的升降器。
答案 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)
我认为现有的镜头库遵循类似的方法,但更多的魔法,比如自动创建镜头。我确实回避了镜头。相反,我很高兴地意识到只需几行代码即可获得我需要的东西。