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

时间:2012-02-01 09:13:23

标签: haskell state monads monad-transformers state-monad

我正在模拟一个4位微处理器。我需要跟踪寄存器,内存和运行输出(还有一个获取 - 执行周期计数器的奖励点)。我已经设法在没有monad的情况下做到了这一点,但是在明确地传递那么多东西时感觉很乱。函数定义也很混乱,冗长且难以阅读。

我试图用monads做这个,但它不适合在一起。我尝试将所有单独的状态组件视为单一类型,但这给我留下了产生价值的问题。

State Program () -- Represents the state of the processor after a single iteration of the fetch execute cycle

是唯一有意义的类型。但那时为什么甚至打扰?我尝试通过从我的复合类型中拉出字符串并将其作为值

来分解它
State Program' String
除了我需要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))))

我还没有进入FEcycle计数器!

问题:

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

2 个答案:

答案 0 :(得分:9)

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

相反,我会建议StateT Program IO ()。状态的界面是相同的,正如您所说,您可以使用IOliftIO中输出。当然,值类型是(),但是那有什么问题?您可以从顶级仿真器返回没有相关值。当然,您可能会将较小的可重用组件作为模拟器的 part ,并且这些组件将具有相关的结果类型。 (事实上​​,get就是这样一个组成部分。)在顶层没有有意义的回报值没有错。

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

当然,它实际上是将您的代码转换为一组全局变量的命令性变异,但这实际上是一个优势,因为正好您正在仿效的CPU所基于的范例。使用data-lens-template包,您可以在一行中从记录定义中推导出这些镜头。

答案 1 :(得分:2)

执行此操作的一种简单方法是创建表示寄存器和内存的数据类型:

data Register = ...
data Memory = ...
data Machine = Machine [Register] Memory

然后有一些更新寄存器/内存的函数。现在将此类型用于您的州和类型的输出:

type Simulation = State Machine Output

现在每个操作都可以采用以下形式:

operation previous = do machine <- get
                        (result, newMachine) <- operate on machine
                        put newMachine
                        return result

此处previous是机器的上一个输出。您也可以将其合并到结果中。

因此Machine类型代表机器的状态;你正在通过它来处理先前操作的输出。

更复杂的方法是使用state threads(Control.Monad.ST)。这些允许您在函数内使用可变引用和数组,同时保证外部纯度。