Haskell中的有限状态传感器?

时间:2015-01-17 07:13:42

标签: haskell state-machine transducer

我一直想知道是否有办法以惯用的方式在Haskell中定义和使用finite state transducers

您可以将FST作为生成器(它生成类型为{x1,x2}的输出)或识别器(给定类型为{x1,x2的输入)它识别它是否属于理性关系),或者作为翻译器(给定输入磁带,它将其转换为输出磁带)。表示会根据方法而改变吗?

是否也可以通过指定重写规则来生成FST模型?例如,创建DSL以模拟重写规则,然后创建函数createFST :: [Rule] -> FST

我能找到的最接近的是Kmett,Bjarnason和Cough的machines图书馆: https://hackage.haskell.org/package/machines

但我似乎无法实现如何使用Machine建模FST。我认为这样做的正确方法与他们定义Moore和Mealy机器的方式类似:将FST定义为不同的实体,但提供Automaton的实例以便能够将其用作一台机器。

我也发现了一些其他选项,但是他们以一种简单的方式定义它(比如在https://hackage.haskell.org/package/fst中)。这并不能让我相信,因为我想知道是否有更好的方法来使用Haskell的类型系统的优势(比如Moore和Mealy机器如何在{{1中定义)库)。

1 个答案:

答案 0 :(得分:26)

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已将输出的类型变量重命名为or旁边的操作,读取到Await,以及写入{{1} }}

Yield

data Step k o r = forall t. Await (t -> r) (k t) r | Yield o r | Stop Await稍微复杂一点,因为Machine可以从多个来源读取。对于只能从单个来源读取的ReadProcess Is应用于特定类型,这是第一种类型的第二种k的证据。对于Is阅读输入Processa将为k

Is a

存在量化data Step (Is a) o r = forall t. Await (t -> r) (Is a t) r | Yield o r | Stop implementation detail for dealing with Sources。在目睹forall t.之后,这就变成了。

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 下一步要做的额外r是在没有更多输入的情况下接下来该做什么。

机器变压器`MachineT`

机器变换器MachineT使Await看起来几乎像我们的Step。它说,“在一个monad FST上运行的机器是该monad中要做的事情,以获得下一个m。每个步骤之后要做的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

对所有Monad s 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
    )

过程

ProcessProcessTMachineMachineT,只能读取单一类型的输入aIs 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模拟状态传感器。