6

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

我试图用 monads 做到这一点,但它不适合在一起。我尝试将所有单独的状态组件视为单一类型,但这给我留下了如何创建值的问题。

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

是唯一有意义的类型。但在这一点上,为什么还要打扰呢?我尝试通过将字符串从复合类型中拉出并将其视为值来分解它

State Program' String

效果很好,除了我需要 RUNNING 输出。无论我做什么,我都无法同时抓住字符串和状态。

现在我正在努力解决单子变压器。似乎我必须分离出所有不同级别的状态。但我的头快要爆炸了。

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. 看到我现在正在拔出单子变压器,是否可以停止将“运行输出”视为状态并将其交给 IO 单子?那太棒了,我可以打印它,而不是坚持它。
  3. 我应该将状态分为多少层?我可以看到两个不同的层,但它们彼此密切相关(内存和寄存器都取决于内存和寄存器的状态)。我应该将它们作为一个单一的状态保持在一起还是将它们分开并堆叠起来?哪种方法会产生最易读的代码?
4

2 回答 2

9

将多个状态 monad 相互叠加是一个坏主意:您必须组合一堆lifts 才能获得每个状态,仅通过堆栈向下的层数来识别。呸!实际上,mtl 库通常被设计为用于堆栈中的每个“种类”的一个 monad 转换器,除了极少数例外。

相反,我会建议StateT Program IO (). 状态的接口是相同的,正如您所说,您可以IO简单地使用liftIO. 当然,值类型是(),但这有什么问题呢?您无法从顶级仿真器返回任何相关值。而且,当然,您可能有更小的、可重用的组件作为仿真器的一部分,并且这些组件将具有相关的结果类型。(事实上​​,get就是这样一个组件。)在顶层没有有意义的返回值并没有错。

就方便地访问该州的每个部分而言,您正在寻找的是镜头;这个 Stack Overflow 答案是一个很好的介绍。它们让您可以简单轻松地访问和修改您的状态的独立部分。例如,使用data-lens实现,您可以轻松地编写类似于regA += 1递增regAstack %= drop 2删除堆栈的前两个元素的内容。

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

于 2012-02-01T09:30:13.837 回答
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类型代表机器的状态;您正在通过它线程化先前操作的输出。

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

于 2012-02-01T09:25:48.270 回答