haskell中不同的,相互作用的状态水平

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计数器!

问题:

  1. 我是在正确的轨道上吗?
  2. 看到我现在正在推出monad变形金刚,是否有可能停止将"运行输出"视为状态并将其移至IO monad?这将是非常棒的,而不是坚持它,我可以打印它.
  3. 我应该将状态分成多少层?我可以看到两个不同的层,但它们彼此紧密相关(存储器和寄存器都取决于存储器和寄存器的状态).我应该将它们作为一个单独的状态保存在一起,还是将它们分开并堆叠起来?哪种方法会产生最易读的代码?

ehi*_*ird 9

将多个状态monad分层放在一起是一个坏主意:你必须编写一堆lifts来获取每个状态,只能通过堆栈下面的层数来识别.呸!实际上,mtl库通常被设计为在极少数例外情况下使用堆栈中每个"种类"的一个monad变换器.

相反,我会建议StateT Program IO ().状态的接口是相同的,正如你所说,你可以IO简单地通过使用输出liftIO.当然,价值类型是(),但这有什么不对?您可以从顶级仿真器返回相关值.当然,您可能会将较小的可重用组件作为模拟器的一部分,并且这些组件将具有相关的结果类型.(事实上​​,get就是这样一个组成部分.)在顶层没有有意义的回报值没有错.

至于方便地访问州的每个部分,你要找的是镜头; 这个Stack Overflow的答案是一个很好的介绍.它们让您可以轻松,轻松地访问和修改您所在州的独立部分.例如,使用数据镜头实现,您可以轻松地编写类似于regA += 1递增的内容regA,或者stack %= drop 2删除堆栈的前两个元素.

当然,它实际上是将您的代码转换为一组全局变量的命令性变异,但这实际上是一个优势,因为这正是您所模拟的CPU所依据的范例.而使用数据镜头模板包,您可以从一行记录定义中得出这些镜头.

  • 轻微的狡辩 - 分层状态monad是一个坏主意 - 没有你真的想要他们_.它们允许对状态进行分区,因此您可以限制某些客户端只具有对状态的特定层的读访问权限而不是读写访问权限,这用于"安全意识"代码.否则很好的答案. (2认同)