streaming库中Stream
的简化(无效)定义如下:
data Stream f = Step (f (Stream f))
| Return
我试图了解引入此函子f
的动机。
典型的f
是Of 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 a
,Stream (Of a) m
和Identity
的函子。第一个实现a
的流,第二个实现'erasure'的流。
我真的不明白。我可以在不使用a
中的f
的情况下完成所有这些事情,即Stream
的简单流,流的流和擦除。
该仿函数f
不能执行其他操作?
答案 0 :(得分:1)
streaming的grouping 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