Mealy 机器交替地从输入流a 中读取a 并将b 输出到输出流。它先读取,然后在每次读取后输出一次。
newtype Mealy a b = Mealy { runMealy :: a -> (b, Mealy a b) }
Moore 机器交替将b 输出到输出流,并从输入流中读取输入a。它以b 的输出开始,然后在每次输出后读取一次。
data Moore a b = Moore b (a -> Moore a b)
FST 要么从其输入读取、写入其输出,要么停止。它可以连续读取任意次数,也可以连续写入任意次数。
data FST a b
= Read (a -> FST a b)
| Write (b, FST a b)
| Stop
相当于来自机器的FST 是Process。它的定义有点分散。为了简化讨论,我们暂时忘记Process,从内到外探索它。
基函数
为了描述Process 是什么,我们将首先注意到到目前为止所有三台机器中的一个模式。它们中的每一个都递归地引用自己的“下一步做什么”。我们将用任何类型next 替换“下一步做什么”。 Mealy 机器在将输入映射到输出的同时,还提供 next 机器运行。
newtype MealyF a b next = MealyF { runMealyF :: a -> (b, next) }
Moore 机器在输出并请求输入后,找出要运行的 next 机器。
data MooreF a b next = MooreF b (a -> next)
我们可以用同样的方式写FST。当我们从输入中Read 时,我们将根据输入找出要做什么next。当我们Write 到输出时,我们还将提供输出后要做什么next。当我们Stop 时,接下来无事可做。
data FSTF a b next
= Read (a -> next)
| Write (b, next)
| Stop
这种消除显式递归的模式在 Haskell 代码中反复出现,通常称为“基函子”。在机器包中,基本函子是Step。与我们的代码相比,Step 将输出的类型变量重命名为 o,在 r 旁边做什么,读取到 Await,写入到 Yield。
data Step k o r
= forall t. Await (t -> r) (k t) r
| Yield o r
| Stop
Awaiting 比Read 稍微复杂一点,因为Machine 可以从多个来源读取。对于只能从单个源读取的Processes,k 是应用于特定类型的Is,这是对第二种类型Is 第一种类型的证明。对于Process 读取输入a,k 将是Is a。
data Step (Is a) o r
= forall t. Await (t -> r) (Is a t) r
| Yield o r
| Stop
存在量化forall t. 是implementation detail for dealing with Sources。在见证了a ~ t之后就变成这样了。
data Step (Is a) o r
= forall t ~ a. Await (t -> r) Refl r
| Yield o r
| Stop
如果我们将t 与a 统一起来并删除始终相同的Refl 构造函数,这看起来就像我们的FSTF。
data Step (Is a) o r
= Await (a -> r) r
| Yield o r
| Stop
Await 中额外的r 是在没有更多输入时下一步要做什么。
机器变压器`MachineT`
机器变压器MachineT 使Step 看起来几乎像我们的FST。它说,“在某个 monad m 上运行的机器是在该 monad 中执行的操作以获得下一个 Step。每个步骤之后要做的 next 是另一个 MachineT。”
newtype MachineT m k o = MachineT { runMachineT :: m (Step k o (MachineT m k o)) }
总的来说,专门针对我们的类型,这看起来像
newtype MachineT m (Is a) o =
MachineT m (
Await (a -> MachineT m (Is a) o) (MachineT m (Is a) o)
| Yield o (MachineT m (Is a) o)
| Stop
)
Machine 是一个纯粹的MachineT。
type Machine k o = forall m. Monad m => MachineT m k o
对所有Monads m 的通用量化是另一种说法,即计算不需要来自底层Monad 的任何东西。这可以通过将Identity 替换为m 来查看。
type Machine k o =
MachineT Identity (
Await (a -> MachineT Identity k o) (MachineT Identity k o)
| Yield o (MachineT Identity k o)
| Stop
)
进程
Process 或 ProcessT 是 Machine 或 MachineT,仅读取单一类型的输入 a、Is a。
type Process a b = Machine (Is a) b
type ProcessT m a b = MachineT m (Is a) b
Process 在删除所有始终相同的中间构造函数后具有以下结构。这个结构和我们的FST 完全一样,只是它在没有更多输入的情况下增加了“下一步做什么”。
type Process a b =
Await (a -> Process a b) (Process a b)
| Yield b (Process a b)
| Stop
ProcessT 变体有一个 m 包裹在其周围,以便它可以在每个步骤中在 monad 中起作用。
Process 模拟状态传感器。