简体   繁体   English

Iteratees和FRP之间有什么联系?

[英]What is the connection between Iteratees and FRP?

It seems to me that there is a strong connection between the two ideas. 在我看来,这两个想法之间存在着密切的联系。 My guess is that FRP could be implemented in terms of Iteratees if there would be a way to express arbitrary graphs with Iteratees. 我的猜测是,如果有一种方法可以用迭代器表示任意图形,那么FRP可以用Iteratees来实现。 But afaik they only support chain-like structures. 但是afaik他们只支持链式结构。

Could someone shed some light on this? 有人可以对此有所了解吗?

It's the other way around. 这是相反的方式。 There is a strong connection between AFRP and stream processing. AFRP和流处理之间存在很强的联系。 In fact AFRP is a form of stream processing, and you can use the idiom to implement something very similar to pipes: 事实上,AFRP 流处理的一种形式,你可以使用这个习惯来实现与管道非常相似的东西:

data Pipe m a b =
    Pipe {
      cleanup :: m (),
      feed    :: [a] -> m (Maybe [b], Pipe m a b)
    }

That's an extension of wire categories as found in Netwire. 这是Netwire中的电线类别的扩展。 It receives the next chunk of input and returns Nothing when it stops producing. 它接收下一个输入块并在停止生成时返回Nothing。 Using this a file reader would have the following type: 使用此文件阅读器将具有以下类型:

readFile :: (MonadIO m) => FilePath -> Pipe m a ByteString

Pipe is a family of applicative functors, so to apply a simple function to the stream elements you could just use fmap : Pipe是一系列应用函子,所以要将一个简单的函数应用于流元素,你可以使用fmap

fmap (B.map toUpper) . readFile

For your convenience it's also a family of profunctors. 为了您的方便,它也是一个系列的教师。

The most interesting feature is that this is a family of Alternative functors. 最有趣的功能是这是一系列替代函子。 That allows you to route streams around and allow multiple stream processors to "try" before giving up. 这允许您路由流并允许多个流处理器在放弃之前“尝试”。 This can be extended to a full-fledged parsing library that can even use some static information for optimization purposes. 这可以扩展到一个完整的解析库,甚至可以使用一些静态信息进行优化。

You can implement a limited form of FRP using stream processors. 您可以使用流处理器实现有限形式的FRP。 For example, using the pipes library, you might define a source of events: 例如,使用pipes库,您可以定义事件源:

mouseCoordinates :: (Proxy p) => () -> Producer p MouseCoord IO r

... and you might similarly define a graphical handler that takes mouse coordinates and updates a cursor on a canvas: ...您可以类似地定义一个图形处理程序,它接受鼠标坐标并更新画布上的光标:

coordHandler :: (Proxy p) => () -> Consumer p MouseCoord IO r

Then you would hook up the mouse events to the handler using composition: 然后,您将使用组合将鼠标事件连接到处理程序:

>>> runProxy $ mouseCoordinates >-> coordHandler

And it would run just the way you expect. 它会像你期望的那样运行。

Like you said, this works well for a single chain of stages, but what about more arbitrary topologies? 就像你说的那样,这适用于单个连锁阶段,但是更多的任意拓扑呢? Well, it turns out that since the central Proxy type of pipes is a monad transformer, you can model any arbitrary topology just by nesting proxy monad transformers on top of themselves. 好吧,事实证明,由于中央Proxy类型的pipes是monad变换器,你可以通过在它们自己之上嵌套代理monad变换器来建模任何任意拓扑。 For example, here is how you would zip two input streams: 例如,以下是如何压缩两个输入流:

zipD
 :: (Monad m, Proxy p1, Proxy p2, Proxy p3)
 => () -> Consumer p1 a (Consumer p2 b (Producer p3 (a, b) m)) r
zipD () = runIdentityP $ hoist (runIdentityP . hoist runIdentityP) $ forever $ do
    a <- request ()               -- Request from the outer Consumer
    b <- lift $ request ()        -- Request from the inner consumer
    lift $ lift $ respond (a, b)  -- Respond to the Producer

This behaves like a curried function. 这表现得像一个curried函数。 You partially apply it to each input sequentially and you can then run it when it is fully applied. 您可以按顺序将其部分应用于每个输入,然后可以在完全应用它时运行它。

-- 1st application
p1 = runProxyK $ zipD   <-< fromListS [1..]

-- 2nd application
p2 = runProxyK $ p2     <-< fromListS [4..6]

-- 3rd application
p3 = runProxy  $ printD <-< p3

It runs just the way you expect: 它以您期望的方式运行:

>>> p3
(1, 4)
(2, 5)
(3, 6)

This trick generalizes to any topology. 这个技巧推广到任何拓扑。 You can find a lot more details about this in Control.Proxy.Tutorial in the "Branches, zips, and merges" section. 您可以在“分支,拉链和合并”部分的Control.Proxy.Tutorial中找到有关此内容的更多详细信息。 In particular, you should check out the fork combinator it uses as an example, which lets you split a stream into two outputs. 特别是,您应该检查它使用的fork组合器作为示例,它允许您将流拆分为两个输出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM