如何在 Haskell 中修改状态的一部分
How to modify parts of a State in Haskell
我有很多修改a的操作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
结合使用,我必须从 System
、运行 foo'
中提取单个 Process
和 ItemDb
然后用结果修改 System
。
这是相当多的解包和包装,产生的代码行比仅将系统作为一个整体传递并让 foo
提取它需要的任何内容要多。在后一种情况下,包装和展开与实际的 foo'
操作混合在一起,我觉得这两个方面应该分开。
我想我需要某种提升操作,将狭窄的 foo'
变成 foo
。我想我可以写这个,但我必须为窄函数的每个签名编写这样一个提升器,结果是很多不同的提升器。
- 有没有成语可以解决这样的问题?
- 值得麻烦吗?
一个常见的解决方案是使用 class,可能是由 Control.Lens.TH.makeClassy
的模板 Haskell 魔法创建的。要点是你传递了整个 System
,但你不让函数 知道 那就是你给它的。它所允许知道的是,您提供的内容提供了获取 and/or 修改它应该处理的片段的方法。
我最终编写了一个适用于任何状态的函数,它需要一个 "Lens" 来捕获从较大状态到较小状态并返回的特定转换
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
它让我可以写
run :: ExitP -> State SimState Log
...
do
evqs' <-focus onSys $ step (t,evt)
...
步骤在 "smaller" 状态下运行
step :: Timed Event -> State Sys.System [EventQu]
这里的 onSys 是一个 "Lens",它的工作原理是这样的:
onSys :: Lens Sys.System SimState
onSys (Sis e s) = (s, Sis e)
哪里
data SimState = Sis {
events :: EventQu,
sisSys :: Sys.System
我想现有的 Lens 库遵循类似的方法,但更神奇,比如自动创建镜头。我确实回避镜头。相反,我很高兴地意识到只需要几行代码就可以得到我需要的东西。
我有很多修改a的操作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
结合使用,我必须从 System
、运行 foo'
中提取单个 Process
和 ItemDb
然后用结果修改 System
。
这是相当多的解包和包装,产生的代码行比仅将系统作为一个整体传递并让 foo
提取它需要的任何内容要多。在后一种情况下,包装和展开与实际的 foo'
操作混合在一起,我觉得这两个方面应该分开。
我想我需要某种提升操作,将狭窄的 foo'
变成 foo
。我想我可以写这个,但我必须为窄函数的每个签名编写这样一个提升器,结果是很多不同的提升器。
- 有没有成语可以解决这样的问题?
- 值得麻烦吗?
一个常见的解决方案是使用 class,可能是由 Control.Lens.TH.makeClassy
的模板 Haskell 魔法创建的。要点是你传递了整个 System
,但你不让函数 知道 那就是你给它的。它所允许知道的是,您提供的内容提供了获取 and/or 修改它应该处理的片段的方法。
我最终编写了一个适用于任何状态的函数,它需要一个 "Lens" 来捕获从较大状态到较小状态并返回的特定转换
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
它让我可以写
run :: ExitP -> State SimState Log
...
do
evqs' <-focus onSys $ step (t,evt)
...
步骤在 "smaller" 状态下运行
step :: Timed Event -> State Sys.System [EventQu]
这里的 onSys 是一个 "Lens",它的工作原理是这样的:
onSys :: Lens Sys.System SimState
onSys (Sis e s) = (s, Sis e)
哪里
data SimState = Sis {
events :: EventQu,
sisSys :: Sys.System
我想现有的 Lens 库遵循类似的方法,但更神奇,比如自动创建镜头。我确实回避镜头。相反,我很高兴地意识到只需要几行代码就可以得到我需要的东西。