我一直想知道是否有办法以惯用的方式在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中定义)库)。
答案 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
已将输出的类型变量重命名为o
,r
旁边的操作,读取到Await
,以及写入{{1} }}
Yield
data Step k o r
= forall t. Await (t -> r) (k t) r
| Yield o r
| Stop
比Await
稍微复杂一点,因为Machine
可以从多个来源读取。对于只能从单个来源读取的Read
,Process
Is
应用于特定类型,这是第一种类型的第二种k
的证据。对于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
是implementation detail for dealing with Source
s。在目睹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
使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
)
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
模拟状态传感器。