1{-# OPTIONS_HADDOCK hide #-}
4-- Module : Streamly.Internal.Data.Sink
5-- Copyright : (c) 2019 Composewell Technologies
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
12-- The 'Sink' type is a just a special case of 'Fold' and we can do without
13-- it. However, in some cases 'Sink' is a simpler type and may provide better
14-- performance than 'Fold' because it does not maintain any state. Folds can
15-- be used for both pure and monadic computations. Sinks are not applicable to
18module Streamly.Internal.Data.Sink
46 -- * Input Transformation
47 -- | These are contravariant operations i.e. they apply on the input of the
48 -- 'Sink', for this reason they are prefixed with 'l' for 'left'.
62import Control.Monad (when, void)
63import Data.Map.Strict (Map)
65 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
66 foldl, map, mapM, sequence, all, any, sum, product, elem,
67 notElem, maximum, minimum, head, last, tail, length, null,
68 reverse, iterate, init, and, or, lookup, foldr1, (!!),
69 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip)
71import Streamly.Internal.Data.Fold.Types (Fold(..))
72import Streamly.Internal.Data.Sink.Types (Sink(..))
74import qualified Data.Map.Strict as Map
76------------------------------------------------------------------------------
78------------------------------------------------------------------------------
80-- | Convert a 'Sink' to a 'Fold'. When you want to compose sinks and folds
81-- together, upgrade a sink to a fold before composing.
82toFold :: Monad m => Sink m a -> Fold m a ()
83toFold (Sink f) = Fold step begin done
89------------------------------------------------------------------------------
90-- Composing with sinks
91------------------------------------------------------------------------------
93-- | Distribute one copy each of the input to both the sinks.
101-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
102-- > sink (Sink.tee (pr \"L") (pr \"R")) (S.enumerateFromTo 1 2)
109tee :: Monad m => Sink m a -> Sink m a -> Sink m a
110tee (Sink fL) (Sink fR) = Sink (\a -> fL a >> fR a)
112-- | Distribute copies of the input to all the sinks in a container.
122-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
123-- > sink (Sink.distribute [(pr \"L"), (pr \"R")]) (S.enumerateFromTo 1 2)
130-- This is the consumer side dual of the producer side 'sequence_' operation.
131{-# INLINE distribute #-}
132distribute :: Monad m => [Sink m a] -> Sink m a
133distribute ss = Sink (\a -> Prelude.mapM_ (\(Sink f) -> f a) ss)
135-- | Demultiplex to multiple consumers without collecting the results. Useful
136-- to run different effectful computations depending on the value of the stream
137-- elements, for example handling network packets of different types using
138-- different handlers.
143-- -----stream m a-----Map-----|
150-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
151-- > let table = Data.Map.fromList [(1, pr \"One"), (2, pr \"Two")]
152-- in Sink.sink (Sink.demux id table) (S.enumerateFromTo 1 100)
157demux :: (Monad m, Ord k) => (a -> k) -> Map k (Sink m a) -> Sink m a
158demux f kv = Sink step
163 -- XXX should we raise an exception in Nothing case?
164 -- Ideally we should enforce that it is a total map over k so that look
166 case Map.lookup (f a) kv of
171demux :: (Monad m, Ord k) => Map k (Sink m a) -> Sink m (a, k)
177 -- XXX should we raise an exception in Nothing case?
178 -- Ideally we should enforce that it is a total map over k so that look
180 case Map.lookup k kv of
184-- | Split elements in the input stream into two parts using a monadic unzip
185-- function, direct each part to a different sink.
190-- -----Stream m a----(b,c)--|
194-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
195-- in Sink.sink (Sink.unzip return (pr \"L") (pr \"R")) (S.yield (1,2))
200unzipM :: Monad m => (a -> m (b,c)) -> Sink m b -> Sink m c -> Sink m a
201unzipM f (Sink stepB) (Sink stepC) =
202 Sink (\a -> f a >>= \(b,c) -> stepB b >> stepC c)
204-- | Same as 'unzipM' but with a pure unzip function.
206unzip :: Monad m => (a -> (b,c)) -> Sink m b -> Sink m c -> Sink m a
207unzip f = unzipM (return . f)
209------------------------------------------------------------------------------
210-- Input transformation
211------------------------------------------------------------------------------
213-- | Map a pure function on the input of a 'Sink'.
214{-# INLINABLE lmap #-}
215lmap :: (a -> b) -> Sink m b -> Sink m a
216lmap f (Sink step) = Sink (step . f)
218-- | Map a monadic function on the input of a 'Sink'.
219{-# INLINABLE lmapM #-}
220lmapM :: Monad m => (a -> m b) -> Sink m b -> Sink m a
221lmapM f (Sink step) = Sink (\x -> f x >>= step)
223-- | Filter the input of a 'Sink' using a pure predicate function.
224{-# INLINABLE lfilter #-}
225lfilter :: Monad m => (a -> Bool) -> Sink m a -> Sink m a
226lfilter f (Sink step) = Sink (\a -> when (f a) $ step a)
228-- | Filter the input of a 'Sink' using a monadic predicate function.
229{-# INLINABLE lfilterM #-}
230lfilterM :: Monad m => (a -> m Bool) -> Sink m a -> Sink m a
231lfilterM f (Sink step) = Sink (\a -> f a >>= \use -> when use $ step a)
233------------------------------------------------------------------------------
235------------------------------------------------------------------------------
237-- | Drain all input, running the effects and discarding the results.
238drain :: Monad m => Sink m a
239drain = Sink (\_ -> return ())
242-- > drainM f = lmapM f drain
244-- Drain all input after passing it through a monadic function.
245{-# INLINABLE drainM #-}
246drainM :: Monad m => (a -> m b) -> Sink m a
247drainM f = Sink (void . f)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list