Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How exactly does Stream Fusion work?

The only resources on Stream Fusion I can find are papers introducing it, which aren't really the best learning sources. How exactly does stream fusion work?

More specifially, as this is the part the paper didn't explain well: how are the co-structures generated after the list->stream conversion (ie, maps f . maps g) themselves folded?

like image 775
MaiaVictor Avatar asked Jan 04 '14 16:01

MaiaVictor


1 Answers

Here's the definition of maps from Duncan Coutt's thesis (section 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′

Now consider the expression

maps f . maps g

A compiler can inline (.) to get

\x -> maps f (maps g x)

We can see from the definition of Stream that it has only one constructor:

data Stream a = ∃ s . Stream (s → Step a s) s

So the previous result is equivalent to:

\(Stream next0 s) -> maps f (maps g (Stream next0 s))

Inlining maps g, which is safe to do as maps is non-recursive (this is the key insight of stream fusion):

\(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′

Inlining 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′

Next we can inline next1 into next2 and simplify the case expressions with "case-of-case" - again note that next1 is non-recursive - and delete the now dead 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′

The key point is that these steps are all small optimisations that make sense in isolation and that don't require special compiler knowledge of either stream fusion itself, or the Stream type or maps function.

like image 112
GS - Apologise to Monica Avatar answered Sep 20 '22 15:09

GS - Apologise to Monica