流媒体库流中的函子背后的直觉

时间:2019-11-26 06:22:11

标签: haskell functor haskell-streaming

streaming库中Stream的简化(无效)定义如下:

data Stream f = Step (f (Stream f))
              | Return

我试图了解引入此函子f的动机。

典型的fOf a,其中Of被定义为

data Of a b = !a :> b
    deriving Functor

当我在f的定义中将Of a读为Stream时,这是有道理的,Of a b有点像a,后面是更多可从b获得。通过这种解释,f (Stream f)的定义中的Stream类似于Of a (Stream (Of a))。但是在这种情况下,本来应该是一个更简单的版本

data Stream a = Step a (Stream a)
              | Return

我很难理解为什么要使用此函子f进行泛化。在引言中,作者说Stream

  

...可用于流传输以任何函子f为特征的连续的不同步骤

  

一般类型Stream f m r表示一系列步骤...的形状由'functor'参数f确定。

但是在Streaming.Prelude中,我只能找到Or aStream (Of a) mIdentity的函子。第一个实现a的流,第二个实现'erasure'的流。

我真的不明白。我可以在不使用a中的f的情况下完成所有这些事情,即Stream的简单流,流的流和擦除。

该仿函数f不能执行其他操作?

1 个答案:

答案 0 :(得分:1)

streaminggrouping functions的主要特征是它们不会强迫您在任何时候将整个组都保留在内存中。他们以“流式”方式执行分组。一旦检测到新组的开始,就可以开始“下游”处理该组。

例如,想象一个像lines :: Stream (Of Text) IO r -> Stream (Stream (Of Text) IO) IO r这样的函数。该函数只要在流中检测到换行符,就会开始一个新组,即使新行的结尾尚未实现。

类型Stream (Stream (Of文本) IO) IO r的优点是,要到达当前行之后的行,我们必须完全消耗当前行。这是因为“流的其余部分”位于functor参数的结果值中。并且Stream只在筋疲力尽时才放弃结果。

Stream (Of (Stream (Of a) IO ()) IO ()这样的类型不会迫使我们在移动到下一行之前耗尽当前行。我们可以简单地忽略内部Stream之前产生的内部Stream。但是,“下一行”可能甚至不存在,因为我们还没有真正阅读过之前的内容!也许可以使这种方案以某种方式起作用,但是语义将不太清楚,需要用户更多的知识。


mapped这样的函数使我们可以使用定义为“上游”的“下游”组。例如,此代码读取 stdin 形式的字符串,其中字符串“-”是组分隔符。下游代码将字符串解析为Int,并在遇到5时显示一条消息。请注意,消息显示在组的上游之前

    import Streaming
    import qualified Streaming.Prelude as S

    example :: Stream (Of ()) IO () 
    example = 
        let upstream :: Stream (Stream (Of String) IO) IO ()
            upstream = S.breaks (=="-") S.stdinLn
            downstream :: Stream (Stream (Of String) IO) IO () 
                       -> Stream (Of ()) IO ()
            downstream = S.mapped handleGroup
            handleGroup :: Stream (Of String) IO r ->
                           IO (Of () r)
            handleGroup stream = do
                print "group began" 
                r <- S.effects
                   . S.chain (\_ -> putStrLn "found!") 
                   . S.filter (==5) 
                   . S.map (read @Int) 
                   $ stream 
                print "group ended"
                return (() :> r)
         in downstream upstream

    main :: IO ()
    main = S.effects example