@@ -8,57 +8,93 @@
88-- Stability : experimental
99-- Portability : GHC
1010--
11−-- 'Unfold' type represents an effectful action that generates a stream of
12−-- values from a single starting value often called a seed value. Values can be
13−-- generated and /pulled/ from the 'Unfold' one at a time. It can also be
14−-- called a producer or a source of stream. It is a data representation of the
15−-- standard 'Streamly.Prelude.unfoldr' function. An 'Unfold' can be converted
16−-- into a stream type using 'Streamly.Prelude.unfold' by supplying the seed.
17−--
18−-- = Performance Notes
19−--
20−-- 'Unfold' representation is more efficient than using streams when combining
21−-- streams. 'Unfold' type allows multiple unfold actions to be composed into a
22−-- single unfold function in an efficient manner by enabling the compiler to
23−-- perform stream fusion optimization.
24−-- @Unfold m a b@ can be considered roughly equivalent to an action @a -> t m
25−-- b@ (where @t@ is a stream type). Instead of using an 'Unfold' one could just
26−-- use a function of the shape @a -> t m b@. However, working with stream types
27−-- like t'Streamly.SerialT' does not allow the compiler to perform stream fusion
28−-- optimization when merging, appending or concatenating multiple streams.
29−-- Even though stream based combinator have excellent performance, they are
30−-- much less efficient when compared to combinators using 'Unfold'. For
31−-- example, the 'Streamly.Prelude.concatMap' combinator which uses @a -> t m b@
32−-- (where @t@ is a stream type) to generate streams is much less efficient
33−-- compared to 'Streamly.Prelude.unfoldMany'.
34−--
35−-- On the other hand, transformation operations on stream types are as
36−-- efficient as transformations on 'Unfold'.
37−--
38−-- We should note that in some cases working with stream types may be more
39−-- convenient compared to working with the 'Unfold' type. However, if extra
40−-- performance boost is important then 'Unfold' based composition should be
41−-- preferred compared to stream based composition when merging or concatenating
42−-- streams.
43−--
44−-- = Programmer Notes
45−--
46−-- > import qualified Streamly.Data.Unfold as UF
47−--
48−-- More, not yet exposed, unfold combinators can be found in
49−-- "Streamly.Internal.Data.Unfold".
50−
51−-- The stream types (e.g. t'Streamly.SerialT') can be considered as a special
52−-- case of 'Unfold' with no starting seed.
11+-- An 'Unfold' is a source or a producer of a stream of values. It takes a
12+-- seed value as an input and unfolds it into a sequence of values.
13+--
14+-- >>> import qualified Streamly.Data.Fold as Fold
15+-- >>> import qualified Streamly.Data.Unfold as Unfold
16+-- >>> import qualified Streamly.Prelude as Stream
17+--
18+-- For example, the 'fromList' Unfold generates a stream of values from a
19+-- supplied list. Unfolds can be converted to 'Streamly.Prelude.SerialT'
20+-- stream using the Stream.unfold operation.
21+--
22+-- >>> stream = Stream.unfold Unfold.fromList [1..100]
23+-- >>> Stream.sum stream
24+-- 5050
25+--
26+-- All the serial stream generation operations in "Streamly.Prelude"
27+-- can be expressed using unfolds:
28+--
29+-- > Stream.fromList = Stream.unfold Unfold.fromList [1..100]
30+--
31+-- Conceptually, an 'Unfold' is just like "Data.List.unfoldr". Let us write a
32+-- step function to unfold a list using "Data.List.unfoldr":
33+--
34+-- >>> :{
35+-- f [] = Nothing
36+-- f (x:xs) = Just (x, xs)
37+-- :}
38+--
39+-- >>> Data.List.unfoldr f [1,2,3]
40+-- [1,2,3]
41+--
42+-- Unfold.unfoldr is just the same, it uses the same step function:
43+--
44+-- >>> Stream.toList $ Stream.unfold (Unfold.unfoldr f) [1,2,3]
45+-- [1,2,3]
46+--
47+-- The input of an unfold can be transformed using 'lmap':
48+--
49+-- >>> u = Unfold.lmap (fmap (+1)) Unfold.fromList
50+-- >>> Stream.toList $ Stream.unfold u [1..5]
51+-- [2,3,4,5,6]
5352--
53+-- 'Unfold' streams can be transformed using transformation combinators. For
54+-- example, to retain only the first two elements of an unfold:
55+--
56+-- >>> u = Unfold.take 2 Unfold.fromList
57+-- >>> Stream.toList $ Stream.unfold u [1..100]
58+-- [1,2]
59+--
60+-- Multiple unfolds can be combined in several interesting ways. For example,
61+-- to generate nested looping as in imperative languages (also known as cross
62+-- product of the two streams):
63+--
64+-- >>> u1 = Unfold.lmap fst Unfold.fromList
65+-- >>> u2 = Unfold.lmap snd Unfold.fromList
66+-- >>> u = Unfold.crossWith (,) u1 u2
67+-- >>> Stream.toList $ Stream.unfold u ([1,2,3], [4,5,6])
68+-- [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]
69+--
70+-- Nested loops using unfolds provide C like performance due to complete stream
71+-- fusion.
72+--
73+-- Please see "Streamly.Internal.Data.Unfold" for additional @Pre-release@
74+-- functions.
75+--
76+-- = Unfolds vs. Streams
77+--
78+-- Unfolds' raison d'etre is their efficiency in nested stream operations due
79+-- to complete stream fusion. 'Streamly.Prelude.concatMap' or the 'Monad'
80+-- instance of streams use stream generation operations of the shape @a -> t m
81+-- b@ and then flatten the resulting stream. This implementation is more
82+-- powerful but does not allow for complete stream fusion. Unfolds provide
83+-- less powerful but more efficient 'Streamly.Prelude.unfoldMany', 'many' and
84+-- 'crossWith' operations as an alternative to a subset of use cases of
85+-- 'concatMap' and 'Applicative' stream operations.
86+--
87+-- "Streamly.Prelude" exports polymorphic stream generation operations that
88+-- provide the same functionality as unfolds in this module. Since unfolds can
89+-- be easily converted to streams, several modules in streamly provide only
90+-- unfolds for serial stream generation. We cannot use unfolds exclusively for
91+-- stream generation as they do not support concurrency.
92+
5493module Streamly.Data.Unfold
5594 (
5695 -- * Unfold Type
5796 Unfold
5897
59− -- * Folding
60− , fold
61−
6298 -- * Unfolds
6399 -- One to one correspondence with
64100 -- "Streamly.Internal.Data.Stream.IsStream.Generate"
@@ -106,14 +142,6 @@ module Streamly.Data.Unfold
106142
107143 -- ** Nesting
108144 , many
109−
110− -- ** Resource Management
111− , finally
112− , bracket
113−
114− -- ** Exceptions
115− , onException
116− , handle
117145 )
118146where
119147