The*_*kle 6 monads state haskell state-monad monad-transformers
我正在模拟一个4位微处理器.我需要跟踪寄存器,内存和运行输出(还有一个获取 - 执行周期计数器的奖励点).我已经设法在没有monad的情况下做到了这一点,但是一下子明确地传递那么多东西感觉很乱.函数定义也很混乱,冗长且难以阅读.
我试图用monad做这个,它只是不适合在一起.我尝试将所有单独的状态组件视为单一类型,但这给我留下了产生价值的问题.
State Program () -- Represents the state of the processor after a single iteration of the fetch execute cycle
Run Code Online (Sandbox Code Playgroud)
是唯一有意义的类型.但那时为什么甚至打扰?我尝试通过从我的复合类型中拉出字符串并将其作为值来对其进行分解
State Program' String
Run Code Online (Sandbox Code Playgroud)
除了我需要RUNNING输出这一事实外,它工作得很好.无论我做了什么,我都无法同时保持弦乐和状态.
现在我正在努力解决monad变形金刚问题.似乎我必须将所有不同级别的州分开.但我的脑袋快速爆炸.
StateT Registers (StateT Memory (State Output)) a =
StateT (registers -> (StateT Memory (State Output)) (a,registers))
StateT Registers (StateT Memory (State Output)) a =
StateT (registers -> (Memory -> (Output -> (((a,Registers),Memory),Output))))
Run Code Online (Sandbox Code Playgroud)
我还没有进入FEcycle计数器!
问题:
将多个状态monad分层放在一起是一个坏主意:你必须编写一堆lift
s来获取每个状态,只能通过堆栈下面的层数来识别.呸!实际上,mtl库通常被设计为在极少数例外情况下使用堆栈中每个"种类"的一个monad变换器.
相反,我会建议StateT Program IO ()
.状态的接口是相同的,正如你所说,你可以IO
简单地通过使用输出liftIO
.当然,价值类型是()
,但这有什么不对?您可以从顶级仿真器返回相关值.当然,您可能会将较小的可重用组件作为模拟器的一部分,并且这些组件将具有相关的结果类型.(事实上,get
就是这样一个组成部分.)在顶层没有有意义的回报值没有错.
至于方便地访问州的每个部分,你要找的是镜头; 这个Stack Overflow的答案是一个很好的介绍.它们让您可以轻松,轻松地访问和修改您所在州的独立部分.例如,使用数据镜头实现,您可以轻松地编写类似于regA += 1
递增的内容regA
,或者stack %= drop 2
删除堆栈的前两个元素.
当然,它实际上是将您的代码转换为一组全局变量的命令性变异,但这实际上是一个优势,因为这正是您所模拟的CPU所依据的范例.而使用数据镜头模板包,您可以从一行记录定义中得出这些镜头.