Stream Fusion的工作原理是什么?

Mai*_*tor 19 haskell functional-programming

我可以找到Stream Fusion的唯一资源是介绍它的论文,这些资源并不是最好的学习资源.流融合究竟是如何工作的?

更具体地说,因为这是本文没有解释的部分:在列表 - >流转换(即maps f . maps g)自身折叠后如何生成共同结构?

GS *_*ica 21

这里的定义maps邓肯Coutt的论文(第1.4.2节):

maps :: (a ? b) ? Stream a ? Stream b
maps f (Stream next0 s0) = Stream next s0
    where
        next s = case next0 s of
            Done ? Done
            Skip s? ? Skip s?
            Yield x s? ? Yield (f x) s?
Run Code Online (Sandbox Code Playgroud)

现在考虑表达式

maps f . maps g
Run Code Online (Sandbox Code Playgroud)

编译器可以内联(.)获取

\x -> maps f (maps g x)
Run Code Online (Sandbox Code Playgroud)

我们可以从定义中Stream看出它只有一个构造函数:

data Stream a = ? s . Stream (s ? Step a s) s
Run Code Online (Sandbox Code Playgroud)

所以之前的结果相当于:

\(Stream next0 s) -> maps f (maps g (Stream next0 s))
Run Code Online (Sandbox Code Playgroud)

内联maps g,这是非安全的maps非递归(这是流融合的关键见解):

\(Stream next0 s) -> maps f (Stream next1 s)
      where
          next1 s = case next0 s of
            Done ? Done
            Skip s? ? Skip s?
            Yield x s? ? Yield (g x) s?
Run Code Online (Sandbox Code Playgroud)

内联maps f:

\(Stream next0 s) -> Stream next2 s
      where
          next1 s = case next0 s of
            Done ? Done
            Skip s? ? Skip s?
            Yield x s? ? Yield (g x) s?
          next2 s = case next1 s of
            Done ? Done
            Skip s? ? Skip s?
            Yield x s? ? Yield (f x) s?
Run Code Online (Sandbox Code Playgroud)

接下来,我们可以内嵌next1next2并简化case与"案例的情况"的表述-再次注意,next1非递归-并删除现在死了next1:

\(Stream next0 s) -> Stream next2 s
      where
          next2 s = case next0 s of
            Done ? Done
            Skip s? ? Skip s?
            Yield x s? ? Yield (f (g x)) s?
Run Code Online (Sandbox Code Playgroud)

关键点在于这些步骤都是小优化,这些优化在隔离中是有意义的,并且不需要特殊的编译器知识,无论是流融合本身,还是Stream类型或maps函数.

  • 对于任何其他读者,请记住`Stream`被定义为`data Stream a =∃s.流(s→Maybe(a,s))s`. (2认同)