on df93350 help sign in
streamly/src/Streamly/Unfold.hs
130 lines 4.5 kB history raw
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE PatternSynonyms #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RecordWildCards #-}
8{-# LANGUAGE ScopedTypeVariables #-}
10#include "inline.hs"
12-- |
13-- Module : Streamly.Unfold
14-- Copyright : (c) 2019 Composewell Technologies
15-- License : BSD3
16-- Maintainer : harendra.kumar@gmail.com
17-- Stability : experimental
18-- Portability : GHC
19--
20-- Streams forcing a closed control flow loop can be categorized under
21-- two types, unfolds and folds, both of these are duals of each other.
22--
23-- Unfold streams are really generators of a sequence of elements, we can also
24-- call them pull style streams. These are lazy producers of streams. On each
25-- evaluation the producer generates the next element. A consumer can
26-- therefore pull elements from the stream whenever it wants to. A stream
27-- consumer can multiplex pull streams by pulling elements from the chosen
28-- streams, therefore, pull streams allow merging or multiplexing. On the
29-- other hand, with this representation we cannot split or demultiplex a
30-- stream. So really these are stream sources that can be generated from a
31-- seed and can be merged or zipped into a single stream.
32--
33-- The dual of Unfolds are Folds. Folds can also be called as push style
34-- streams or reducers. These are strict consumers of streams. We keep pushing
35-- elements to a fold and we can extract the result at any point. A driver can
36-- choose which fold to push to and can also push the same element to multiple
37-- folds. Therefore, folds allow splitting or demultiplexing a stream. On the
38-- other hand, we cannot merge streams using this representation. So really
39-- these are stream consumers that reduce the stream to a single value, these
40-- consumers can be composed such that a stream can be split over multiple
41-- consumers.
43-- Open control flow style streams can also have two representations. StreamK
44-- is a producer style representation. We can also have a consumer style
45-- representation. We can use that for composable folds in StreamK
46-- representation.
47--
48module Streamly.Unfold
49 (
50 -- * Unfold Type
51 -- |
52 -- A 'Fold' can be run over a seed using the 'unfold' combinator.
53 --
54 -- >>> unfold UF.replicateM (putStrLn "hello") 10
56 Unfold
57 , unfold
59 -- ** Unfolds
60 , singleton
61 , replicateM
62 , fromList
63 )
64where
66import Streamly.Streams.StreamD.Type (Stream(..), Step(..))
67#if __GLASGOW_HASKELL__ < 800
68import Streamly.Streams.StreamD.Type (pattern Stream)
69#endif
70import Streamly.Unfold.Types (Unfold(..))
72-------------------------------------------------------------------------------
73-- Running unfolds
74-------------------------------------------------------------------------------
76-- | Convert an 'Unfold' into a 'Stream' by supplying it a seed.
77--
78{-# INLINE_NORMAL unfold #-}
79unfold :: Monad m => a -> Unfold m a b -> Stream m b
80unfold seed (Unfold ustep inject) = Stream step Nothing
81 where
82 {-# INLINE_LATE step #-}
83 step _ Nothing = inject seed >>= return . Skip . Just
84 step _ (Just st) = do
85 r <- ustep st
86 return $ case r of
87 Yield x s -> Yield x (Just s)
88 Skip s -> Skip (Just s)
89 Stop -> Stop
91-------------------------------------------------------------------------------
92-- Unfolds
93-------------------------------------------------------------------------------
95-- | Identity unfold. Generates a singleton stream with the seed as the only
96-- element in the stream.
97--
98-- > singleton = replicateM 1
99--
100{-# INLINE singleton #-}
101singleton :: Monad m => Unfold m a a
102singleton = Unfold step inject
103 where
104 inject x = return $ Just x
105 {-# INLINE_LATE step #-}
106 step (Just x) = return $ Yield x Nothing
107 step Nothing = return Stop
109-- | Generates a stream replicating the seed @n@ times.
110--
111{-# INLINE replicateM #-}
112replicateM :: Monad m => Int -> Unfold m a a
113replicateM n = Unfold step inject
114 where
115 inject x = return (x, n)
116 {-# INLINE_LATE step #-}
117 step (x, i) = return $
118 if i <= 0
119 then Stop
120 else Yield x (x, (i - 1))
122-- | Convert a list of pure values to a 'Stream'
123{-# INLINE_LATE fromList #-}
124fromList :: Monad m => Unfold m [a] a
125fromList = Unfold step inject
126 where
127 inject x = return x
128 {-# INLINE_LATE step #-}
129 step (x:xs) = return $ Yield x xs
130 step [] = return Stop