2012-02-01 27 views
6

我正在模拟一个4位微处理器。我需要跟踪寄存器,内存和运行输出(还有一个获取执行周期计数器的奖励点)。我已经设法做到了这一点,但没有monad,但是感觉很混乱,一次性传递了很多东西。此外,功能定义很混乱,很长,很难阅读。haskell中不同的交互级别状态

我试图用monads做到这一点,它只是不适合在一起。我尝试将所有单独的状态组件作为单一类型处理,但这给我留下了什么使价值的问题。

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

是唯一有意义的类型。但是那个时候为什么还要打扰呢?我试图通过拉动串出我的复合型的,并把它当作值

State Program' String 

这除了事实上,我需要运行输出伟大的工作,打破它。无论我做了什么,我都无法同时坚持弦乐和状态。

现在我试图解决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. 我应该将状态分成多少层?我可以看到两个不同的层,但它们彼此紧密依赖(内存和寄存器都取决于内存和寄存器的状态)。我应该将它们作为一个单独的状态保存在一起,还是将它们分开并叠加起来?哪种方法会产生最易读的代码?
+1

也许“运行输出”可能最好使用Writer monad表示(请参阅http://monads.haskell.cz/html/writermonad.html)? – 2012-02-01 09:22:58

回答

9

将多个状态单元彼此分层是一个糟糕的主意:您必须编写一堆lift才能获得每个状态,仅由堆栈中的层数确定。呸!实际上,一般而言,mtl库被设计用于在极少数例外情况下使用堆栈中每种“种类”的一个monad变换器。我想建议。状态界面相同,如您所说,您可以通过简单地使用liftIOIO中执行输出。当然,值类型是(),但是那有什么问题? 可以从顶级模拟器返回没有相关的价值。而且,当然,您可能会有更小的可重用组件,如模拟器的部分,并且这些组件会具有相关的结果类型。 (事实上​​,get就是这样一个组件。)在顶层没有任何有意义的回报值没有任何问题。

只要方便地访问状态的每个部分,你要找的是镜头; this Stack Overflow answer是一个很好的介绍。他们让您轻松访问和修改您的州的独立部分。例如,通过执行data-lens,您可以轻松地编写诸如regA += 1之类的内容来增加regAstack %= drop 2以删除堆栈的前两个元素。

当然,它本质上是将你的代码转换为一组全局变量的当务之急突变,但其实这是一个优势,因为这是正是范式你模仿的CPU是基于,而且与data-lens-template包,你可以从一条记录定义中得到这些镜头。

+0

那真是太棒了。学习模板haskell的时间。是否很容易找到像regA + = 1这样的功能定义支持的东西?因为虽然这很好,可读,并且最清楚地表达了(非常必要的)意图,但它看起来不像功能性代码。 – TheIronKnuckle 2012-02-01 10:05:59

+1

你不必学习模板哈斯克尔使用数据镜头模板;只需在文件顶部贴上'{ - #LANGUAGE TemplateHaskell# - }'和'Program'定义下的'makeLenses ['Program]'。至于'(+ =)'的定义,当然;它们只是'StateT'的核心镜头API的简单包装; [来源](http://hackage.haskell.org/packages/archive/data-lens/2.0.2/doc/html/src/Data-Lens-Strict.html)与Hackage文档链接。 – ehird 2012-02-01 10:18:30

+2

小问题 - 分层状态monad是一个坏主意 - _无论你真的想要他们_。它们允许对状态进行分区,因此您可以限制某些客户端只能读取特定的状态层,而不是读写访问权限,这用于“安全意识”代码。否则很好的答案。 – 2012-02-01 18:39:08

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)。这些可以让你在函数内部使用可变引用和数组,同时在外部保证纯度。