在Haskell中将流变换器函数转换为Mealy自动机

在Haskell中将流变换器函数转换为Mealy自动机,haskell,stream,transformer,Haskell,Stream,Transformer,我与streams合作: data Stream a = Cons a (Stream a) 特别是流转换器的功能: f :: Stream a -> Stream b 我想用这样一个函数制作一个Mealy自动机: data Mealy a b = Mealy (a -> (b, Mealy a b)) 有没有办法写这样一个函数 toMealy :: (Stream a -> Stream b) -> Mealy a b machine :: Stream (Su

我与streams合作:

data Stream a = Cons a (Stream a)
特别是流转换器的功能:

f :: Stream a -> Stream b
我想用这样一个函数制作一个Mealy自动机:

data Mealy a b = Mealy (a -> (b, Mealy a b))
有没有办法写这样一个函数

toMealy :: (Stream a -> Stream b) -> Mealy a b
machine :: Stream (Sum (Of String) ((->) (Feed Int))) Identity ()         
machine = unseparate (transformation trickster)
我找不到办法。尽管另一种方法很容易实现:

toStreamTransformer :: Mealy a b -> Stream a -> Stream b

也许我遗漏了一些琐碎的东西?

如果你假设传递给
toMealy
的流转换器是“确定性”的,那么你可以通过输入
a
a:e:e:…
传递给流转换器(其中
e=error“输入流转换器不是确定性的”)来启动你的Mealy机器
),并输出输出流的第一个元素
b
。然后修改流转换器,将
a
前置到其输入,并删除其输出的第一个元素(可能是
b
)并递归

或者,如果您愿意,您可以使用input
a:a:a:…
;然后,您将得到一个总函数,该函数不检测输入是否是确定性的,而是生成一个Mealy机器,该机器仅在输入是确定性的时候生成原始的流转换器


这种方法将在运行Mealy机器的步数上花费二次时间;可能有更聪明的方法。

这个答案利用了包提供的
抽象。这个抽象:

  • 是一个单子转换器,所以你可以把任何单子放在它下面
  • 具有与流生成的元素分离的返回类型。流耗尽时返回此类型的值
假设您有一个函数,如:

module Main where

import Streaming
import Streaming.Internal (Stream(Effect,Step,Return))
import Streaming.Prelude

transformation :: Monad m => Stream (Of Int) m r -> Stream (Of String) m r
transformation = undefined -- your code here
转换
Int
值流更改为
字符串
值流。它在基单子上是多态的,这意味着转换本身是纯的。它在返回类型
r
上是多态的,这意味着转换总是耗尽源流

现在,我们编写以下辅助定义:

data Feed a = Input a | EOF

trickster :: Monad m => Stream (Of a) (Stream ((->) (Feed a)) m) ()        
trickster = do
  r <- Effect (Step (Return . Return)) -- request a `Feed a` value
  case r of
      Input a -> Step (a :> trickster)
      EOF     -> Return () 
我们可以使用该功能通过
机器
前进

inspect :: (Functor f, Monad m) => Stream f m r -> m (Either r (f (Stream f m r)))
这里有一种可怕的类型:

ghci> :t runIdentity (inspect machine)
runIdentity (inspect machine)
  :: Either
       ()
       (Sum
          (Of String)
          ((->) (Feed Int))
          (Stream (Sum (Of String) ((->) (Feed Int))) Identity ()))
它基本上意味着,在给定的步骤中,机器要么终止(但是
trickster
的实现确保它永远不会终止,除非我们通过
EOF
),要么它产生一个
字符串
,或者要求我们输入一个
Int

(我们本可以不使用
取消分离
,但剥离两个
层的过程会更加混乱。)


(另外,请参阅Paul Chiusano的博客文章,了解此代码背后的原始想法。)

我看到的问题是没有起点;每个Mealy自动机都来自以前的一些自动机,但是第一个来自哪里?换句话说,您需要一个显式的开始状态作为
toMealy
的参数。如果您不关心函数的作用,您当然可以编写一个函数
toMealy::(流a->流b)->Mealy a b
,但我猜您心里有更具体的想法。但并非所有的流变压器都能由
到流变压器
生产;只有输出的前N个元素只依赖于输入的前N个元素的那些N。这不是你问题的答案,但你可以看看Edward Kmetts的“机器”包@Paul Johnson我知道这个软件包,但正如你所说,它似乎没有解决方案。我在我的库中也实现了类似的东西,只是转换不是为了使用机器;但是对于库的
折叠
数据类型,非常感谢。花了相当长的时间才明白。这篇博文也帮了大忙!
trickster
的定义比它需要的更复杂,因为它访问
Stream
的内部以获得一点效率。你可以用
r产生一个*>trickster
代替
Input a->Step(a:>trickster)
return()
代替
return()
<代码>效果
步骤
返回
是流式处理中的内部构造函数。内部
通常是隐藏的。