Remove obselete files and fix documentation and dev module imports

This commit is contained in:
Adithya Kumar 2023-07-27 18:35:26 +05:30
parent 5f4ba50e09
commit 128ac005df
23 changed files with 90 additions and 6250 deletions

View File

@ -18,7 +18,7 @@ import Prelude hiding ()
-- import qualified Prelude as P
-- import qualified Data.List as List
import qualified Streamly.Internal.Data.Stream.StreamDK as S
import qualified Streamly.Internal.Data.Stream.StreamK.Alt as S
-- import qualified Streamly.Internal.Data.Stream.Common as SP
-- import qualified Streamly.Internal.Data.SVar as S

View File

@ -37,7 +37,7 @@ module Streamly.Data.Stream
-- * Construction
-- | Functions ending in the general shape @b -> Stream m a@.
--
-- See also: "Streamly.Internal.Data.Stream.Generate" for
-- See also: "Streamly.Internal.Data.Stream.StreamD.Generate" for
-- @Pre-release@ functions.
--
-- Useful Idioms:
@ -111,7 +111,7 @@ module Streamly.Data.Stream
-- | Functions ending in the general shape @Stream m a -> m b@ or @Stream m
-- a -> m (b, Stream m a)@
--
-- See also: "Streamly.Internal.Data.Stream.Eliminate" for @Pre-release@
-- See also: "Streamly.Internal.Data.Stream.StreamD.Eliminate" for @Pre-release@
-- functions.
-- EXPLANATION: In imperative terms a fold can be considered as a loop over the stream
@ -253,7 +253,7 @@ module Streamly.Data.Stream
-- * Scanning
-- | Stateful one-to-one transformations.
--
-- See also: "Streamly.Internal.Data.Stream.Transform" for
-- See also: "Streamly.Internal.Data.Stream.StreamD.Transform" for
-- @Pre-release@ functions.
{-
@ -471,7 +471,7 @@ module Streamly.Data.Stream
-- For example, instead of calling them on a stream of chars call them on a
-- stream of arrays before flattening it to a stream of chars.
--
-- See also: "Streamly.Internal.Data.Stream.Exception" for
-- See also: "Streamly.Internal.Data.Stream.StreamD.Exception" for
-- @Pre-release@ functions.
, onException

View File

@ -1,505 +0,0 @@
-- |
-- Module : Streamly.Data.Stream.StreamDK
-- Copyright : (c) 2017 Composewell Technologies
--
-- License : BSD3
-- Maintainer : streamly@composewell.com
-- Stability : released
-- Portability : GHC
--
-- To run examples in this module:
--
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Stream.StreamDK as Stream
--
-- We will add some more imports in the examples as needed.
--
-- For effectful streams we will use the following IO action:
--
-- >>> effect n = print n >> return n
--
-- = Overview
--
-- Streamly is a framework for modular data flow based programming and
-- declarative concurrency. Powerful stream fusion framework in streamly
-- allows high performance combinatorial programming even when using byte level
-- streams. Streamly API is similar to Haskell lists.
--
-- Streams can be constructed like lists, except that they use 'nil' instead of
-- '[]' and 'cons' instead of ':'.
--
-- `cons` adds a pure value at the head of the stream:
--
-- >>> import Streamly.Data.Stream.StreamDK (Stream, cons, consM, nil)
-- >>> stream = 1 `cons` 2 `cons` nil :: Stream IO Int
-- >>> Stream.fold Fold.toList stream -- IO [Int]
-- [1,2]
--
-- `consM` adds an effect at the head of the stream:
--
-- >>> stream = effect 1 `consM` effect 2 `consM` nil
-- >>> Stream.fold Fold.toList stream
-- 1
-- 2
-- [1,2]
--
-- == Console Echo Example
--
-- In the following example, 'repeatM' generates an infinite stream of 'String'
-- by repeatedly performing the 'getLine' IO action. 'mapM' then applies
-- 'putStrLn' on each element in the stream converting it to stream of '()'.
-- Finally, 'drain' folds the stream to IO discarding the () values, thus
-- producing only effects.
--
-- >>> import Data.Function ((&))
--
-- >>> :{
-- echo =
-- Stream.repeatM getLine -- Stream IO String
-- & Stream.mapM putStrLn -- Stream IO ()
-- & Stream.fold Fold.drain -- IO ()
-- :}
--
-- This is a console echo program. It is an example of a declarative loop
-- written using streaming combinators. Compare it with an imperative @while@
-- loop.
--
-- Hopefully, this gives you an idea how we can program declaratively by
-- representing loops using streams. In this module, you can find all
-- "Data.List" like functions and many more powerful combinators to perform
-- common programming tasks.
--
-- == Useful Idioms
--
-- >>> fromListM = Stream.sequence . Stream.fromList
-- >>> fromFoldableM = Stream.sequence . Stream.fromFoldable
-- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
--
-- Also see "Streamly.Internal.Data.Stream.StreamDK" module for many more @Pre-release@
-- combinators. See the <https://github.com/composewell/streamly-examples>
-- repository for many more real world examples of stream programming.
--
-- == Performance Notes
--
-- Operations annotated as /CPS/ force the use of continuation passing style
-- streams. Therefore, such operations are not subjected to stream fusion.
-- However, for some of these operations you can find fusible alternatives in
-- the internal modules, which are perfectly fine to use but you need to
-- understand the implications especially the O(n^2) nature of those
-- operations..
--
module Streamly.Data.Stream.StreamDK
(
Stream
-- * Construction
-- | Functions ending in the general shape @b -> Stream m a@.
--
-- See also: "Streamly.Internal.Data.Stream.Generate" for
-- @Pre-release@ functions.
-- ** Primitives
-- | Primitives to construct a stream from pure values or monadic actions.
-- All other stream construction and generation combinators described later
-- can be expressed in terms of these primitives. However, the special
-- versions provided in this module can be much more efficient in most
-- cases. Users can create custom combinators using these primitives.
, nil
, nilM
, cons
, consM
-- , cons2 -- fused version
-- , consM2 -- fused version
-- ** Unfolding
-- | 'unfoldrM' is the most general way of generating a stream efficiently.
-- All other generation operations can be expressed using it.
, unfoldr
, unfoldrM
-- ** From Values
-- | Generate a monadic stream from a seed value or values.
, fromPure
, fromEffect
, repeat
, repeatM
, replicate
, replicateM
-- Note: Using enumeration functions e.g. 'Prelude.enumFromThen' turns out
-- to be slightly faster than the idioms like @[from, then..]@.
--
-- ** Enumeration
-- | We can use the 'Enum' type class to enumerate a type producing a list
-- and then convert it to a stream:
--
-- @
-- 'fromList' $ 'Prelude.enumFromThen' from then
-- @
--
-- However, this is not particularly efficient.
-- The 'Enumerable' type class provides corresponding functions that
-- generate a stream instead of a list, efficiently.
, Enumerable (..)
, enumerate
, enumerateTo
-- ** Iteration
, iterate
, iterateM
-- ** From Containers
-- | Convert an input structure, container or source into a stream. All of
-- these can be expressed in terms of primitives.
, fromList
, fromFoldable
-- ** From Unfolds
-- | Most of the above stream generation operations can also be expressed
-- using the corresponding unfolds in the "Streamly.Data.Unfold" module.
, unfold -- XXX rename to fromUnfold?
-- * Elimination
-- | Functions ending in the general shape @Stream m a -> m b@ or @Stream m
-- a -> m (b, Stream m a)@
--
-- See also: "Streamly.Internal.Data.Stream.Eliminate" for @Pre-release@
-- functions.
-- EXPLANATION: In imperative terms a fold can be considered as a loop over the stream
-- that reduces the stream to a single value.
-- Left and right folds both use a fold function @f@ and an identity element
-- @z@ (@zero@) to deconstruct a recursive data structure and reconstruct a
-- new data structure. The new structure may be a recursive construction (a
-- container) or a non-recursive single value reduction of the original
-- structure.
--
-- Both right and left folds are mathematical duals of each other, they are
-- functionally equivalent. Operationally, a left fold on a left associated
-- structure behaves exactly in the same way as a right fold on a right
-- associated structure. Similarly, a left fold on a right associated structure
-- behaves in the same way as a right fold on a left associated structure.
-- However, the behavior of a right fold on a right associated structure is
-- operationally different (even though functionally equivalent) than a left
-- fold on the same structure.
--
-- On right associated structures like Haskell @cons@ lists or Streamly
-- streams, a lazy right fold is naturally suitable for lazy recursive
-- reconstruction of a new structure, while a strict left fold is naturally
-- suitable for efficient reduction. In right folds control is in the hand of
-- the @puller@ whereas in left folds the control is in the hand of the
-- @pusher@.
--
-- The behavior of right and left folds are described in detail in the
-- individual fold's documentation. To illustrate the two folds for right
-- associated @cons@ lists:
--
-- > foldr :: (a -> b -> b) -> b -> [a] -> b
-- > foldr f z [] = z
-- > foldr f z (x:xs) = x `f` foldr f z xs
-- >
-- > foldl :: (b -> a -> b) -> b -> [a] -> b
-- > foldl f z [] = z
-- > foldl f z (x:xs) = foldl f (z `f` x) xs
--
-- @foldr@ is conceptually equivalent to:
--
-- > foldr f z [] = z
-- > foldr f z [x] = f x z
-- > foldr f z xs = foldr f (foldr f z (tail xs)) [head xs]
--
-- @foldl@ is conceptually equivalent to:
--
-- > foldl f z [] = z
-- > foldl f z [x] = f z x
-- > foldl f z xs = foldl f (foldl f z (init xs)) [last xs]
--
-- Left and right folds are duals of each other.
--
-- @
-- foldr f z xs = foldl (flip f) z (reverse xs)
-- foldl f z xs = foldr (flip f) z (reverse xs)
-- @
--
-- More generally:
--
-- @
-- foldr f z xs = foldl g id xs z where g k x = k . f x
-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
-- @
--
-- NOTE: Folds are inherently serial as each step needs to use the result of
-- the previous step. However, it is possible to fold parts of the stream in
-- parallel and then combine the results using a monoid.
-- ** Primitives
-- Consuming a part of the stream and returning the rest. Functions
-- ending in the general shape @Stream m a -> m (b, Stream m a)@
, uncons
-- ** Folding
-- XXX Need to have a general parse operation here which can be used to
-- express all others.
, fold -- XXX rename to run? We can have a Stream.run and Fold.run.
-- XXX fold1 can be achieved using Monoids or Refolds.
-- XXX We can call this just "break" and parseBreak as "munch"
, foldBreak
-- XXX should we have a Fold returning function in stream module?
-- , foldAdd
-- , buildl
-- ** Parsing
, parse
, parseBreak
-- -- ** Lazy Right Folds
-- Consuming a stream to build a right associated expression, suitable
-- for lazy evaluation. Evaluation of the input happens when the output of
-- the fold is evaluated, the fold output is a lazy thunk.
--
-- This is suitable for stream transformation operations, for example,
-- operations like mapping a function over the stream.
-- , foldrM
-- , foldr
-- * Mapping
-- | Stateless one-to-one transformations. Use 'fmap' for mapping a pure
-- function on a stream.
-- EXPLANATION:
-- In imperative terms a map operation can be considered as a loop over
-- the stream that transforms the stream into another stream by performing
-- an operation on each element of the stream.
--
-- 'map' is the least powerful transformation operation with strictest
-- guarantees. A map, (1) is a stateless loop which means that no state is
-- allowed to be carried from one iteration to another, therefore,
-- operations on different elements are guaranteed to not affect each
-- other, (2) is a strictly one-to-one transformation of stream elements
-- which means it guarantees that no elements can be added or removed from
-- the stream, it can merely transform them.
, sequence
, mapM
-- , trace -- XXX Use "tracing" map instead?
, tap
, delay
-- * Scanning
-- | Stateful one-to-one transformations.
--
-- See also: "Streamly.Internal.Data.Stream.Transform" for
-- @Pre-release@ functions.
, scan
, postscan
-- XXX postscan1 can be implemented using Monoids or Refolds.
-- Indexing can be considered as a special type of zipping where we zip a
-- stream with an index stream.
, indexed
-- * Insertion
-- | Add elements to the stream.
-- Inserting elements is a special case of interleaving/merging streams.
, insertBy
, intersperseM
, intersperseM_
, intersperse
-- * Filtering
-- | Remove elements from the stream.
-- ** Stateless Filters
-- | 'mapMaybeM' is the most general stateless filtering operation. All
-- other filtering operations can be expressed using it.
-- EXPLANATION:
-- In imperative terms a filter over a stream corresponds to a loop with a
-- @continue@ clause for the cases when the predicate fails.
, mapMaybe
, mapMaybeM
, filter
, filterM
-- Filter and concat
, catMaybes
, catLefts
, catRights
, catEithers
-- ** Stateful Filters
-- | 'scanMaybe' is the most general stateful filtering operation. The
-- filtering folds (folds returning a 'Maybe' type) in
-- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
-- perform stateful filtering operations in general.
, scanMaybe
, take
, takeWhile
, takeWhileM
, drop
, dropWhile
, dropWhileM
-- XXX These are available as scans in folds. We need to check the
-- performance though. If these are common and we need convenient stream
-- ops then we can expose these.
-- , deleteBy
-- , uniq
-- , uniqBy
-- -- ** Sampling
-- , strideFromThen
-- -- ** Searching
-- Finding the presence or location of an element, a sequence of elements
-- or another stream within a stream.
-- -- ** Searching Elements
-- , findIndices
-- , elemIndices
-- * Combining Two Streams
-- ** Appending
, append
-- ** Interleaving
, interleave
-- , interleave2
-- ** Merging
-- | Merging of @n@ streams can be performed by combining the streams pair
-- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used
-- with 'concatMapWith' it will have O(n^2) performance.
, mergeBy
, mergeByM
-- , mergeBy2
-- , mergeByM2
-- ** Zipping
-- | Zipping of @n@ streams can be performed by combining the streams pair
-- wise using 'mergeMapWith' with O(n * log n) time complexity. If used
-- with 'concatMapWith' it will have O(n^2) performance.
, zipWith
, zipWithM
-- , zipWith2
-- , zipWithM2
, ZipStream (..)
-- ** Cross Product
-- XXX The argument order in this operation is such that it seems we are
-- transforming the first stream using the second stream because the second
-- stream is evaluated many times or buffered and better be finite, first
-- stream could potentially be infinite. In the tradition of using the
-- transformed stream at the end we can have a flipped version called
-- "crossMap" or "nestWith".
, crossWith
-- , cross
-- , joinInner
, CrossStream (..)
-- * Unfold Each
, unfoldMany
, intercalate
, intercalateSuffix
-- * Stream of streams
-- | Stream operations like map and filter represent loop processing in
-- imperative programming terms. Similarly, the imperative concept of
-- nested loops are represented by streams of streams. The 'concatMap'
-- operation represents nested looping.
-- A 'concatMap' operation loops over the input stream and then for each
-- element of the input stream generates another stream and then loops over
-- that inner stream as well producing effects and generating a single
-- output stream.
--
-- One dimension loops are just a special case of nested loops. For
-- example, 'concatMap' can degenerate to a simple map operation:
--
-- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
--
-- Similarly, 'concatMap' can perform filtering by mapping an element to a
-- 'nil' stream:
--
-- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
--
, concatEffect
, concatMapWith
, concatMap
, concatMapM
, mergeMapWith
-- * Repeated Fold
, foldMany -- XXX Rename to foldRepeat
, parseMany
, chunksOf
-- * Buffered Operations
-- | Operations that require buffering of the stream.
-- Reverse is essentially a left fold followed by an unfold.
, reverse
, sortBy
-- * Multi-Stream folds
-- | Operations that consume multiple streams at the same time.
, eqBy
, cmpBy
, isPrefixOf
, isSubsequenceOf
-- trimming sequences
, stripPrefix
-- Exceptions and resource management depend on the "exceptions" package
-- XXX We can have IO Stream operations not depending on "exceptions"
-- in Exception.Base
-- * Exceptions
-- | Most of these combinators inhibit stream fusion, therefore, when
-- possible, they should be called in an outer loop to mitigate the cost.
-- For example, instead of calling them on a stream of chars call them on a
-- stream of arrays before flattening it to a stream of chars.
--
-- See also: "Streamly.Internal.Data.Stream.Exception" for
-- @Pre-release@ functions.
, onException
, handle
-- * Resource Management
-- | 'bracket' is the most general resource management operation, all other
-- operations can be expressed using it. These functions have IO suffix
-- because the allocation and cleanup functions are IO actions. For
-- generalized allocation and cleanup functions see the functions without
-- the IO suffix in the "streamly" package.
, before
, afterIO
, finallyIO
, bracketIO
, bracketIO3
-- * Transforming Inner Monad
, morphInner
, liftInner
, runReaderT
, runStateT
-- -- * Stream Types
-- $serial
-- , Interleave
-- , Zip
)
where
import Streamly.Internal.Data.Stream.StreamDK
import Prelude
hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
notElem, maximum, minimum, head, last, tail, length, null,
reverse, iterate, init, and, or, lookup, foldr1, (!!),
scanl, scanl1, repeat, replicate, concatMap, span)

View File

@ -1,16 +0,0 @@
-- |
-- Module : Streamly.Data.Stream.Zip
-- Copyright : (c) 2017 Composewell Technologies
--
-- License : BSD3
-- Maintainer : streamly@composewell.com
-- Stability : released
-- Portability : GHC
--
module Streamly.Data.Stream.Zip
(
ZipStream (..)
)
where
import Streamly.Internal.Data.Stream.Zip

View File

@ -39,7 +39,7 @@ module Streamly.Data.Unfold
-- * Unfolds
-- One to one correspondence with
-- "Streamly.Internal.Data.Stream.Generate"
-- "Streamly.Internal.Data.Stream.StreamD.Generate"
-- ** Basic Constructors
, unfoldrM

View File

@ -66,13 +66,19 @@ where
import Control.Arrow (second)
import Data.Functor.Identity (Identity, runIdentity)
import GHC.Exts (IsList(..), IsString(..))
import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
import Streamly.Internal.Data.Stream.Type (Stream)
import Streamly.Internal.Data.Stream.Zip (ZipStream(..))
import Text.Read (readPrec)
import Streamly.Internal.Data.Stream.StreamD.Type
( CrossStream
, mkCross
, unCross
)
import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
import Text.Read
( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
, readListPrecDefault)
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
import qualified Streamly.Internal.Data.Stream.Type as Stream
import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
import qualified Streamly.Internal.Data.Stream.StreamD.Generate as Stream
-- XXX Rename to PureStream.
@ -82,21 +88,21 @@ import qualified Streamly.Internal.Data.Stream.Type as Stream
newtype List a = List { toCrossStream :: CrossStream Identity a }
deriving
( Eq, Ord
, Semigroup, Monoid, Functor, Foldable
, Applicative, Traversable, Monad, IsList)
, Functor, Foldable
, Applicative, Monad, IsList)
toStream :: List a -> Stream Identity a
toStream = unCrossStream . toCrossStream
toStream = unCross . toCrossStream
fromStream :: Stream Identity a -> List a
fromStream xs = List (CrossStream xs)
fromStream xs = List (mkCross xs)
instance (a ~ Char) => IsString (List a) where
{-# INLINE fromString #-}
fromString = List . fromList
instance Show a => Show (List a) where
show (List x) = show $ unCrossStream x
show (List x) = show $ unCross x
instance Read a => Read (List a) where
readPrec = fromStream <$> readPrec
@ -119,7 +125,7 @@ pattern Nil <- (runIdentity . K.null . Stream.toStreamK . toStream -> True)
where
Nil = List $ CrossStream (Stream.fromStreamK K.nil)
Nil = List $ mkCross (Stream.fromStreamK K.nil)
infixr 5 `Cons`
@ -128,17 +134,77 @@ infixr 5 `Cons`
--
pattern Cons :: a -> List a -> List a
pattern Cons x xs <-
(fmap (second (List . CrossStream . Stream.fromStreamK))
(fmap (second (List . mkCross . Stream.fromStreamK))
. runIdentity . K.uncons . Stream.toStreamK . toStream
-> Just (x, xs)
)
where
Cons x xs = List $ CrossStream $ Stream.cons x (toStream xs)
Cons x xs = List $ mkCross $ Stream.cons x (toStream xs)
{-# COMPLETE Nil, Cons #-}
------------------------------------------------------------------------------
-- ZipStream
------------------------------------------------------------------------------
-- $setup
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Stream as Stream
-- >>> import qualified Streamly.Internal.Data.Stream.Zip as Stream
------------------------------------------------------------------------------
-- Serially Zipping Streams
------------------------------------------------------------------------------
-- | For 'ZipStream':
--
-- @
-- (<>) = 'Streamly.Data.Stream.append'
-- (\<*>) = 'Streamly.Data.Stream.zipWith' id
-- @
--
-- Applicative evaluates the streams being zipped serially:
--
-- >>> s1 = Stream.ZipStream $ Stream.fromFoldable [1, 2]
-- >>> s2 = Stream.ZipStream $ Stream.fromFoldable [3, 4]
-- >>> s3 = Stream.ZipStream $ Stream.fromFoldable [5, 6]
-- >>> s = (,,) <$> s1 <*> s2 <*> s3
-- >>> Stream.fold Fold.toList (Stream.unZipStream s)
-- [(1,3,5),(2,4,6)]
--
newtype ZipStream m a = ZipStream {unZipStream :: Stream m a}
deriving (Functor)
deriving instance IsList (ZipStream Identity a)
deriving instance (a ~ Char) => IsString (ZipStream Identity a)
deriving instance Eq a => Eq (ZipStream Identity a)
deriving instance Ord a => Ord (ZipStream Identity a)
deriving instance (Foldable m, Monad m) => Foldable (ZipStream m)
instance Show a => Show (ZipStream Identity a) where
showsPrec p dl = showParen (p > 10) $
showString "fromList " . shows (toList dl)
instance Read a => Read (ZipStream Identity a) where
readPrec = parens $ prec 10 $ do
Ident "fromList" <- lexP
fromList <$> readPrec
readListPrec = readListPrecDefault
type ZipSerialM = ZipStream
-- | An IO stream whose applicative instance zips streams serially.
--
type ZipSerial = ZipSerialM IO
instance Monad m => Applicative (ZipStream m) where
pure = ZipStream . Stream.repeat
{-# INLINE (<*>) #-}
ZipStream m1 <*> ZipStream m2 = ZipStream $ Stream.zipWith id m1 m2
------------------------------------------------------------------------------
-- ZipList
------------------------------------------------------------------------------
@ -149,8 +215,8 @@ pattern Cons x xs <-
newtype ZipList a = ZipList { toZipStream :: ZipStream Identity a }
deriving
( Show, Read, Eq, Ord
, Semigroup, Monoid, Functor, Foldable
, Applicative, Traversable, IsList
, Functor, Foldable
, Applicative, IsList
)
instance (a ~ Char) => IsString (ZipList a) where
@ -160,7 +226,7 @@ instance (a ~ Char) => IsString (ZipList a) where
-- | Convert a 'ZipList' to a regular 'List'
--
fromZipList :: ZipList a -> List a
fromZipList (ZipList zs) = List $ CrossStream (unZipStream zs)
fromZipList (ZipList zs) = List $ mkCross (unZipStream zs)
-- | Convert a regular 'List' to a 'ZipList'
--

View File

@ -1,670 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Bottom
-- Copyright : (c) 2017 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- Bottom level Stream module that can be used by all other upper level
-- Stream modules.
module Streamly.Internal.Data.Stream.Bottom
(
-- * Generation
fromPure
, fromEffect
, fromList
, timesWith
, absTimesWith
, relTimesWith
-- * Folds
, fold
, foldBreak
, foldBreak2
, foldEither
, foldEither2
, foldConcat
-- * Builders
, foldAdd
, foldAddLazy
-- * Scans
, smapM
-- $smapM_Notes
, postscan
, catMaybes
, scanMaybe
, take
, takeWhile
, takeEndBy
, drop
, findIndices
-- * Merge
, intersperseM
-- * Fold and Unfold
, reverse
, reverse'
-- * Expand
, concatEffect
, concatEffect2
, concatMapM
, concatMap
-- * Reduce
, foldManyPost
-- * Zipping
, zipWithM
, zipWith
)
where
#include "inline.hs"
import Control.Monad.IO.Class (MonadIO(..))
import GHC.Types (SPEC(..))
import Streamly.Internal.Data.Fold.Type (Fold (..))
import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
import Streamly.Internal.Data.Unbox (Unbox)
import Streamly.Internal.Data.Producer.Type (Producer(..))
import Streamly.Internal.System.IO (defaultChunkSize)
import Streamly.Internal.Data.SVar.Type (defState)
import qualified Streamly.Internal.Data.Array.Type as A
import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.StreamK as K
import qualified Streamly.Internal.Data.Stream as D
import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
import Streamly.Internal.Data.Stream.Type
--
-- $setup
-- >>> :m
-- >>> import Control.Monad (join, (>=>), (<=<))
-- >>> import Data.Function (fix, (&))
-- >>> import Data.Functor.Identity (Identity)
-- >>> import Data.Maybe (fromJust, isJust)
-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
-- >>> import Streamly.Data.Array (Array)
-- >>> import Streamly.Data.Fold (Fold)
-- >>> import Streamly.Data.Stream (Stream)
-- >>> import System.IO.Unsafe (unsafePerformIO)
-- >>> import qualified Streamly.Data.Array as Array
-- >>> import qualified Streamly.Data.MutArray as MArray
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Parser as Parser
-- >>> import qualified Streamly.Data.Unfold as Unfold
-- >>> import qualified Streamly.Internal.Data.Fold as Fold (toStream)
-- >>> import Streamly.Internal.Data.Stream as Stream
------------------------------------------------------------------------------
-- Generation - Time related
------------------------------------------------------------------------------
-- | @timesWith g@ returns a stream of time value tuples. The first component
-- of the tuple is an absolute time reference (epoch) denoting the start of the
-- stream and the second component is a time relative to the reference.
--
-- The argument @g@ specifies the granularity of the relative time in seconds.
-- A lower granularity clock gives higher precision but is more expensive in
-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
--
-- >>> import Control.Concurrent (threadDelay)
-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)
-- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Pre-release/
--
{-# INLINE timesWith #-}
timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)
timesWith g = fromStreamD $ D.timesWith g
-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
-- granularity @g@ specified in seconds. A low granularity clock is more
-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
-- as 1 ms.
--
-- >>> f = Fold.drainMapM print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Pre-release/
--
{-# INLINE absTimesWith #-}
absTimesWith :: MonadIO m => Double -> Stream m AbsTime
absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
-- | @relTimesWith g@ returns a stream of relative time values starting from 0,
-- using a clock of granularity @g@ specified in seconds. A low granularity
-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
-- ms is treated as 1 ms.
--
-- >>> f = Fold.drainMapM print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Pre-release/
--
{-# INLINE relTimesWith #-}
relTimesWith :: MonadIO m => Double -> Stream m RelTime64
relTimesWith = fmap snd . timesWith
------------------------------------------------------------------------------
-- Elimination - Running a Fold
------------------------------------------------------------------------------
-- | Append a stream to a fold lazily to build an accumulator incrementally.
--
-- Example, to continue folding a list of streams on the same sum fold:
--
-- >>> streams = [Stream.fromList [1..5], Stream.fromList [6..10]]
-- >>> f = Prelude.foldl Stream.foldAddLazy Fold.sum streams
-- >>> Stream.fold f Stream.nil
-- 55
--
{-# INLINE foldAddLazy #-}
foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b
foldAddLazy f s = D.foldAddLazy f $ toStreamD s
-- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce
-- |
-- >>> foldAdd = flip Fold.addStream
--
foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)
foldAdd f = fold (Fold.duplicate f)
-- >>> fold f = Fold.extractM . Stream.foldAddLazy f
-- >>> fold f = Stream.fold Fold.one . Stream.foldManyPost f
-- >>> fold f = Fold.extractM <=< Stream.foldAdd f
-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
-- expression strictly at each step. The behavior is similar to 'foldl''. A
-- 'Fold' can terminate early without consuming the full stream. See the
-- documentation of individual 'Fold's for termination behavior.
--
-- Definitions:
--
-- >>> fold f = fmap fst . Stream.foldBreak f
-- >>> fold f = Stream.parse (Parser.fromFold f)
--
-- Example:
--
-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
-- 5050
--
{-# INLINE fold #-}
fold :: Monad m => Fold m a b -> Stream m a -> m b
fold fl strm = D.fold fl $ D.fromStreamK $ toStreamK strm
-- Alternative name foldSome, but may be confused vs foldMany.
-- | Like 'fold' but also returns the remaining stream. The resulting stream
-- would be 'Stream.nil' if the stream finished before the fold.
--
-- /CPS/
--
{-# INLINE foldBreak #-}
foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
foldBreak fl strm = fmap f $ K.foldBreak fl (toStreamK strm)
where
f (b, str) = (b, fromStreamK str)
-- XXX The quadratic slowdown in recursive use is because recursive function
-- cannot be inlined and StreamD/StreamK conversions pile up and cannot be
-- eliminated by rewrite rules.
-- | Like 'foldBreak' but fuses.
--
-- /Note:/ Unlike 'foldBreak', recursive application on the resulting stream
-- would lead to quadratic slowdown. If you need recursion with fusion (within
-- one iteration of recursion) use StreamD.foldBreak directly.
--
-- /Internal/
{-# INLINE foldBreak2 #-}
foldBreak2 :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
foldBreak2 fl strm = fmap f $ D.foldBreak fl $ toStreamD strm
where
f (b, str) = (b, fromStreamD str)
-- | Fold resulting in either breaking the stream or continuation of the fold.
-- Instead of supplying the input stream in one go we can run the fold multiple
-- times, each time supplying the next segment of the input stream. If the fold
-- has not yet finished it returns a fold that can be run again otherwise it
-- returns the fold result and the residual stream.
--
-- /Internal/
{-# INLINE foldEither #-}
foldEither :: Monad m =>
Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
foldEither fl strm = fmap (fmap f) $ K.foldEither fl $ toStreamK strm
where
f (b, str) = (b, fromStreamK str)
-- | Like 'foldEither' but fuses. However, recursive application on resulting
-- stream would lead to quadratic slowdown.
--
-- /Internal/
{-# INLINE foldEither2 #-}
foldEither2 :: Monad m =>
Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
foldEither2 fl strm = fmap (fmap f) $ D.foldEither fl $ toStreamD strm
where
f (b, str) = (b, fromStreamD str)
-- XXX Array folds can be implemented using this.
-- foldContainers? Specialized to foldArrays.
-- | Generate streams from individual elements of a stream and fold the
-- concatenation of those streams using the supplied fold. Return the result of
-- the fold and residual stream.
--
-- For example, this can be used to efficiently fold an Array Word8 stream
-- using Word8 folds.
--
-- The outer stream forces CPS to allow scalable appends and the inner stream
-- forces direct style for stream fusion.
--
-- /Internal/
{-# INLINE foldConcat #-}
foldConcat :: Monad m =>
Producer m a b -> Fold m b c -> Stream m a -> m (c, Stream m a)
foldConcat
(Producer pstep pinject pextract)
(Fold fstep begin done)
stream = do
res <- begin
case res of
Fold.Partial fs -> go fs streamK
Fold.Done fb -> return (fb, fromStreamK streamK)
where
streamK = toStreamK stream
go !acc m1 = do
let stop = do
r <- done acc
return (r, fromStreamK K.nil)
single a = do
st <- pinject a
res <- go1 SPEC acc st
case res of
Left fs -> do
r <- done fs
return (r, fromStreamK K.nil)
Right (b, s) -> do
x <- pextract s
return (b, fromStreamK (K.fromPure x))
yieldk a r = do
st <- pinject a
res <- go1 SPEC acc st
case res of
Left fs -> go fs r
Right (b, s) -> do
x <- pextract s
return (b, fromStreamK (x `K.cons` r))
in K.foldStream defState yieldk single stop m1
{-# INLINE go1 #-}
go1 !_ !fs st = do
r <- pstep st
case r of
D.Yield x s -> do
res <- fstep fs x
case res of
Fold.Done b -> return $ Right (b, s)
Fold.Partial fs1 -> go1 SPEC fs1 s
D.Skip s -> go1 SPEC fs s
D.Stop -> return $ Left fs
------------------------------------------------------------------------------
-- Transformation
------------------------------------------------------------------------------
{-
-- |
-- >>> map = fmap
--
-- Same as 'fmap'.
--
-- >>> Stream.fold Fold.toList $ fmap (+1) $ Stream.fromList [1,2,3]
-- [2,3,4]
--
{-# INLINE map #-}
map :: Monad m => (a -> b) -> Stream m a -> Stream m b
map f = fromStreamD . D.map f . toStreamD
-}
-- | Postscan a stream using the given monadic fold.
--
-- The following example extracts the input stream up to a point where the
-- running average of elements is no more than 10:
--
-- >>> import Data.Maybe (fromJust)
-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
-- >>> s = Stream.enumerateFromTo 1.0 100.0
-- >>> :{
-- Stream.fold Fold.toList
-- $ fmap (fromJust . fst)
-- $ Stream.takeWhile (\(_,x) -> x <= 10)
-- $ Stream.postscan (Fold.tee Fold.latest avg) s
-- :}
-- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]
--
{-# INLINE postscan #-}
postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b
postscan fld = fromStreamD . D.postscan fld . toStreamD
-- $smapM_Notes
--
-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
-- a read-only environment. However, that would just be 'mapM'.
--
-- The initial action could be @m (s, Maybe b)@, and we can also add a final
-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
-- functionality and also to flush the state in the end like scanlMAfter'.
-- We can also use it along with a fusible version of bracket to get
-- scanlMAfter' like functionality. See issue #677.
--
-- This can be further generalized to a type similar to Fold/Parser, giving it
-- filtering and parsing capability as well (this is in fact equivalent to
-- parseMany):
--
-- smapM :: (s -> a -> m (Step s b)) -> m s -> Stream m a -> Stream m b
--
-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
-- Hopefully, this is a better alternative to @scan@. Separation of state from
-- the output makes it easier to think in terms of a shared state, and also
-- makes it easier to keep the state fully strict and the output lazy.
--
-- See also: 'postscan'
--
-- /Pre-release/
--
{-# INLINE smapM #-}
smapM :: Monad m =>
(s -> a -> m (s, b))
-> m s
-> Stream m a
-> Stream m b
smapM step initial stream =
-- XXX implement this directly instead of using postscan
let f = Fold.foldlM'
(\(s, _) a -> step s a)
(fmap (,undefined) initial)
in fmap snd $ postscan f stream
-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
--
-- >>> catMaybes = Stream.mapMaybe id
-- >>> catMaybes = fmap fromJust . Stream.filter isJust
--
-- /Pre-release/
--
{-# INLINE catMaybes #-}
catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
-- catMaybes = fmap fromJust . filter isJust
catMaybes = fromStreamD . D.catMaybes . toStreamD
-- | Use a filtering fold on a stream.
--
-- >>> scanMaybe f = Stream.catMaybes . Stream.postscan f
--
{-# INLINE scanMaybe #-}
scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
scanMaybe p = catMaybes . postscan p
------------------------------------------------------------------------------
-- Transformation - Trimming
------------------------------------------------------------------------------
-- | Take first 'n' elements from the stream and discard the rest.
--
{-# INLINE take #-}
take :: Monad m => Int -> Stream m a -> Stream m a
-- take n = scanMaybe (Fold.taking n)
take n m = fromStreamD $ D.take n $ toStreamD m
-- | End the stream as soon as the predicate fails on an element.
--
{-# INLINE takeWhile #-}
takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
-- takeWhile p = scanMaybe (Fold.takingEndBy_ (not . p))
takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m
{-# INLINE takeEndBy #-}
takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
-- takeEndBy p = scanMaybe (Fold.takingEndBy p)
takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
-- | Discard first 'n' elements from the stream and take the rest.
--
{-# INLINE drop #-}
drop :: Monad m => Int -> Stream m a -> Stream m a
-- drop n = scanMaybe (Fold.dropping n)
drop n m = fromStreamD $ D.drop n $ toStreamD m
------------------------------------------------------------------------------
-- Searching
------------------------------------------------------------------------------
-- | Find all the indices where the element in the stream satisfies the given
-- predicate.
--
-- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
--
{-# INLINE findIndices #-}
findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
-- findIndices p = scanMaybe (Fold.findIndices p)
findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
------------------------------------------------------------------------------
-- Transformation by Inserting
------------------------------------------------------------------------------
-- intersperseM = intersperseMWith 1
-- | Insert an effect and its output before consuming an element of a stream
-- except the first one.
--
-- >>> input = Stream.fromList "hello"
-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input
-- h.,e.,l.,l.,o"h,e,l,l,o"
--
-- Be careful about the order of effects. In the above example we used trace
-- after the intersperse, if we use it before the intersperse the output would
-- be he.l.l.o."h,e,l,l,o".
--
-- >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input
-- he.l.l.o."h,e,l,l,o"
--
{-# INLINE intersperseM #-}
intersperseM :: Monad m => m a -> Stream m a -> Stream m a
intersperseM m = fromStreamD . D.intersperseM m . toStreamD
------------------------------------------------------------------------------
-- Transformation by Reordering
------------------------------------------------------------------------------
-- XXX Use a compact region list to temporarily store the list, in both reverse
-- as well as in reverse'.
--
-- /Note:/ 'reverse'' is much faster than this, use that when performance
-- matters.
--
-- | Returns the elements of the stream in reverse order. The stream must be
-- finite. Note that this necessarily buffers the entire stream in memory.
--
-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
--
{-# INLINE reverse #-}
reverse :: Stream m a -> Stream m a
reverse s = fromStreamK $ K.reverse $ toStreamK s
-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
--
-- /O(n) space/
--
-- /Pre-release/
{-# INLINE reverse' #-}
reverse' :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
reverse' =
fromStreamD
. A.flattenArraysRev -- unfoldMany A.readRev
. D.fromStreamK
. K.reverse
. D.toStreamK
. A.chunksOf defaultChunkSize
. toStreamD
------------------------------------------------------------------------------
-- Combine streams and flatten
------------------------------------------------------------------------------
-- | Map a stream producing monadic function on each element of the stream
-- and then flatten the results into a single stream. Since the stream
-- generation function is monadic, unlike 'concatMap', it can produce an
-- effect at the beginning of each iteration of the inner loop.
--
-- See 'unfoldMany' for a fusible alternative.
--
{-# INLINE concatMapM #-}
concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
-- | Map a stream producing function on each element of the stream and then
-- flatten the results into a single stream.
--
-- >>> concatMap f = Stream.concatMapM (return . f)
-- >>> concatMap f = Stream.concatMapWith Stream.append f
-- >>> concatMap f = Stream.concat . fmap f
-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
--
-- See 'unfoldMany' for a fusible alternative.
--
{-# INLINE concatMap #-}
concatMap ::Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
-- >>> concatEffect = Stream.concat . lift -- requires (MonadTrans t)
-- >>> concatEffect = join . lift -- requires (MonadTrans t, Monad (Stream m))
-- | Given a stream value in the underlying monad, lift and join the underlying
-- monad with the stream monad.
--
-- >>> concatEffect = Stream.concat . Stream.fromEffect
--
-- See also: 'concat', 'sequence'
--
-- See 'concatEffect2' for a fusible alternative.
--
-- /CPS/
--
{-# INLINE concatEffect #-}
concatEffect :: Monad m => m (Stream m a) -> Stream m a
concatEffect generator =
fromStreamK $ K.concatEffect $ fmap toStreamK generator
{-# INLINE concatEffect2 #-}
concatEffect2 :: Monad m => m (Stream m a) -> Stream m a
-- concatEffect generator = concatMapM (\() -> generator) (fromPure ())
concatEffect2 generator =
fromStreamD $ D.concatEffect $ fmap toStreamD generator
-- XXX Need a more intuitive name, and need to reconcile the names
-- foldMany/fold/parse/parseMany/parseManyPost etc.
-- | Like 'foldMany' but evaluates the fold before the stream, and yields its
-- output even if the stream is empty, therefore, always results in a non-empty
-- output even on an empty stream (default result of the fold).
--
-- Example, empty stream:
--
-- >>> f = Fold.take 2 Fold.sum
-- >>> fmany = Stream.fold Fold.toList . Stream.foldManyPost f
-- >>> fmany $ Stream.fromList []
-- [0]
--
-- Example, last fold empty:
--
-- >>> fmany $ Stream.fromList [1..4]
-- [3,7,0]
--
-- Example, last fold non-empty:
--
-- >>> fmany $ Stream.fromList [1..5]
-- [3,7,5]
--
-- Note that using a closed fold e.g. @Fold.take 0@, would result in an
-- infinite stream without consuming the input.
--
-- /Pre-release/
--
{-# INLINE foldManyPost #-}
foldManyPost
:: Monad m
=> Fold m a b
-> Stream m a
-> Stream m b
foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
------------------------------------------------------------------------------
-- Zipping
------------------------------------------------------------------------------
-- | Like 'zipWith' but using a monadic zipping function.
--
{-# INLINE zipWithM #-}
zipWithM :: Monad m =>
(a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
zipWithM f m1 m2 = fromStreamK $ K.zipWithM f (toStreamK m1) (toStreamK m2)
-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
-- elements @a@ and @b@ are then zipped using the supplied zip function and the
-- result @c@ is yielded to the consumer.
--
-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
--
-- >>> s1 = Stream.fromList [1,2,3]
-- >>> s2 = Stream.fromList [4,5,6]
-- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2
-- [5,7,9]
--
{-# INLINE zipWith #-}
zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
zipWith f m1 m2 = fromStreamK $ K.zipWith f (toStreamK m1) (toStreamK m2)

View File

@ -1,143 +0,0 @@
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Streamly.Internal.Data.Stream.Cross
-- Copyright : (c) 2017 Composewell Technologies
--
-- License : BSD3
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
module Streamly.Internal.Data.Stream.Cross
(
CrossStream (..)
)
where
import Control.Monad.Catch (MonadThrow, throwM)
import Control.Monad.Trans.Class (MonadTrans(lift))
import Control.Applicative (liftA2)
import Control.Monad.IO.Class (MonadIO(..))
import Data.Functor.Identity (Identity(..))
import GHC.Exts (IsList(..), IsString(..))
import Streamly.Internal.Data.Stream.Type (Stream)
import qualified Streamly.Internal.Data.Stream.Type as Stream
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
-- $setup
-- >>> import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Stream as Stream
------------------------------------------------------------------------------
-- Stream with a cross product style monad instance
------------------------------------------------------------------------------
-- | A newtype wrapper for the 'Stream' type with a cross product style monad
-- instance.
--
-- Semigroup instance appends two streams.
--
-- A 'Monad' bind behaves like a @for@ loop:
--
-- >>> :{
-- Stream.fold Fold.toList $ unCrossStream $ do
-- x <- CrossStream (Stream.fromList [1,2]) -- foreach x in stream
-- return x
-- :}
-- [1,2]
--
-- Nested monad binds behave like nested @for@ loops:
--
-- >>> :{
-- Stream.fold Fold.toList $ unCrossStream $ do
-- x <- CrossStream (Stream.fromList [1,2]) -- foreach x in stream
-- y <- CrossStream (Stream.fromList [3,4]) -- foreach y in stream
-- return (x, y)
-- :}
-- [(1,3),(1,4),(2,3),(2,4)]
--
newtype CrossStream m a = CrossStream {unCrossStream :: Stream m a}
deriving (Functor, Semigroup, Monoid, Foldable)
-- Pure (Identity monad) stream instances
deriving instance Traversable (CrossStream Identity)
deriving instance IsList (CrossStream Identity a)
deriving instance (a ~ Char) => IsString (CrossStream Identity a)
deriving instance Eq a => Eq (CrossStream Identity a)
deriving instance Ord a => Ord (CrossStream Identity a)
deriving instance Show a => Show (CrossStream Identity a)
deriving instance Read a => Read (CrossStream Identity a)
------------------------------------------------------------------------------
-- Applicative
------------------------------------------------------------------------------
-- Note: we need to define all the typeclass operations because we want to
-- INLINE them.
instance Monad m => Applicative (CrossStream m) where
{-# INLINE pure #-}
pure x = CrossStream (Stream.fromPure x)
{-# INLINE (<*>) #-}
(CrossStream s1) <*> (CrossStream s2) =
CrossStream (Stream.crossApply s1 s2)
-- (<*>) = K.crossApply
{-# INLINE liftA2 #-}
liftA2 f x = (<*>) (fmap f x)
{-# INLINE (*>) #-}
(CrossStream s1) *> (CrossStream s2) =
CrossStream (Stream.crossApplySnd s1 s2)
-- (*>) = K.crossApplySnd
{-# INLINE (<*) #-}
(CrossStream s1) <* (CrossStream s2) =
CrossStream (Stream.crossApplyFst s1 s2)
-- (<*) = K.crossApplyFst
------------------------------------------------------------------------------
-- Monad
------------------------------------------------------------------------------
instance Monad m => Monad (CrossStream m) where
return = pure
-- Benchmarks better with StreamD bind and pure:
-- toList, filterAllout, *>, *<, >> (~2x)
--
-- pure = Stream . D.fromStreamD . D.fromPure
-- m >>= f = D.fromStreamD $ D.concatMap (D.toStreamD . f) (D.toStreamD m)
-- Benchmarks better with CPS bind and pure:
-- Prime sieve (25x)
-- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
--
{-# INLINE (>>=) #-}
(>>=) (CrossStream m) f =
CrossStream
(Stream.fromStreamK
$ K.bindWith
K.append
(Stream.toStreamK m)
(Stream.toStreamK . unCrossStream . f))
{-# INLINE (>>) #-}
(>>) = (*>)
------------------------------------------------------------------------------
-- Transformers
------------------------------------------------------------------------------
instance (MonadIO m) => MonadIO (CrossStream m) where
liftIO x = CrossStream (Stream.fromEffect $ liftIO x)
instance MonadTrans CrossStream where
{-# INLINE lift #-}
lift x = CrossStream (Stream.fromEffect x)
instance (MonadThrow m) => MonadThrow (CrossStream m) where
throwM = lift . throwM

View File

@ -1,377 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Eliminate
-- Copyright : (c) 2017 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- This module contains functions ending in the shape:
--
-- @
-- Stream m a -> m b
-- @
--
-- We call them stream folding functions, they reduce a stream @Stream m a@ to
-- a monadic value @m b@.
module Streamly.Internal.Data.Stream.Eliminate
(
-- * Running Examples
-- $setup
-- * Running a 'Fold'
-- See "Streamly.Internal.Data.Fold".
fold
, foldBreak
, foldBreak2
, foldEither
, foldEither2
, foldConcat
-- * Builders
, foldAdd
, foldAddLazy
-- * Running a 'Parser'
-- "Streamly.Internal.Data.Parser".
, parse
--, parseK
, parseD
--, parseBreak
, parseBreakD
-- * Stream Deconstruction
-- | foldr and foldl do not provide the remaining stream. 'uncons' is more
-- general, as it can be used to implement those as well. It allows to use
-- the stream one element at a time, and we have the remaining stream all
-- the time.
, uncons
, init
-- * Right Folds
, foldrM
, foldr
-- * Left Folds
-- Lazy left folds are useful only for reversing the stream
, foldlS
-- * Multi-Stream folds
-- Full equivalence
, eqBy
, cmpBy
-- finding subsequences
, isPrefixOf
, isInfixOf
, isSuffixOf
, isSubsequenceOf
-- trimming sequences
, stripPrefix
-- , stripInfix
, stripSuffix
)
where
#include "inline.hs"
import Control.Monad.IO.Class (MonadIO(..))
import Foreign.Storable (Storable)
import Streamly.Internal.Data.Parser (Parser (..), ParseError (..))
import Streamly.Internal.Data.Unbox (Unbox)
import Streamly.Internal.Data.Stream.Type (Stream)
import qualified Streamly.Internal.Data.Array.Type as Array
import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.Parser as PRD
import qualified Streamly.Internal.Data.Parser.ParserK.Type as PRK
import qualified Streamly.Internal.Data.Stream as D
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
import qualified Streamly.Internal.Data.StreamK as K
import Streamly.Internal.Data.Stream.Bottom
import Streamly.Internal.Data.Stream.Type hiding (Stream)
import Prelude hiding (foldr, init, reverse)
-- $setup
-- >>> :m
-- >>> import Streamly.Internal.Data.Stream (Stream)
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
-- >>> import qualified Streamly.Internal.Data.Parser as Parser
-- >>> import qualified Streamly.Internal.Data.Fold as Fold
-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
------------------------------------------------------------------------------
-- Deconstruction
------------------------------------------------------------------------------
-- | Decompose a stream into its head and tail. If the stream is empty, returns
-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
-- the head of the stream and @ma@ its tail.
--
-- Properties:
--
-- >>> Nothing <- Stream.uncons Stream.nil
-- >>> Just ("a", t) <- Stream.uncons (Stream.cons "a" Stream.nil)
--
-- This can be used to consume the stream in an imperative manner one element
-- at a time, as it just breaks down the stream into individual elements and we
-- can loop over them as we deem fit. For example, this can be used to convert
-- a streamly stream into other stream types.
--
-- All the folds in this module can be expressed in terms of 'uncons', however,
-- this is generally less efficient than specific folds because it takes apart
-- the stream one element at a time, therefore, does not take adavantage of
-- stream fusion.
--
-- 'foldBreak' is a more general way of consuming a stream piecemeal.
--
-- >>> :{
-- uncons xs = do
-- r <- Stream.foldBreak Fold.one xs
-- return $ case r of
-- (Nothing, _) -> Nothing
-- (Just h, t) -> Just (h, t)
-- :}
--
-- /CPS/
--
{-# INLINE uncons #-}
uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
uncons m = fmap (fmap (fmap fromStreamK)) $ K.uncons (toStreamK m)
-- | Extract all but the last element of the stream, if any.
--
-- Note: This will end up buffering the entire stream.
--
-- /Pre-release/
{-# INLINE init #-}
init :: Monad m => Stream m a -> m (Maybe (Stream m a))
init m = fmap (fmap fromStreamK) $ K.init $ toStreamK m
------------------------------------------------------------------------------
-- Right Folds
------------------------------------------------------------------------------
-- | Right associative/lazy pull fold. @foldrM build final stream@ constructs
-- an output structure using the step function @build@. @build@ is invoked with
-- the next input element and the remaining (lazy) tail of the output
-- structure. It builds a lazy output expression using the two. When the "tail
-- structure" in the output expression is evaluated it calls @build@ again thus
-- lazily consuming the input @stream@ until either the output expression built
-- by @build@ is free of the "tail" or the input is exhausted in which case
-- @final@ is used as the terminating case for the output structure. For more
-- details see the description in the previous section.
--
-- Example, determine if any element is 'odd' in a stream:
--
-- >>> s = Stream.fromList (2:4:5:undefined)
-- >>> step x xs = if odd x then return True else xs
-- >>> Stream.foldrM step (return False) s
-- True
--
{-# INLINE foldrM #-}
foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
foldrM step acc m = D.foldrM step acc $ toStreamD m
-- | Right fold, lazy for lazy monads and pure streams, and strict for strict
-- monads.
--
-- Please avoid using this routine in strict monads like IO unless you need a
-- strict right fold. This is provided only for use in lazy monads (e.g.
-- Identity) or pure streams. Note that with this signature it is not possible
-- to implement a lazy foldr when the monad @m@ is strict. In that case it
-- would be strict in its accumulator and therefore would necessarily consume
-- all its input.
--
-- >>> foldr f z = Stream.foldrM (\a b -> f a <$> b) (return z)
--
{-# INLINE foldr #-}
foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
foldr f z = foldrM (\a b -> f a <$> b) (return z)
------------------------------------------------------------------------------
-- Left Folds
------------------------------------------------------------------------------
-- | Lazy left fold to a stream.
{-# INLINE foldlS #-}
foldlS ::
(Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b
foldlS f z =
fromStreamK
. K.foldlS
(\xs x -> toStreamK $ f (fromStreamK xs) x)
(toStreamK z)
. toStreamK
------------------------------------------------------------------------------
-- Running a Parser
------------------------------------------------------------------------------
-- | Parse a stream using the supplied ParserD 'PRD.Parser'.
--
-- /Internal/
--
{-# INLINE_NORMAL parseD #-}
parseD :: Monad m => PRD.Parser a m b -> Stream m a -> m (Either ParseError b)
parseD p = D.parseD p . toStreamD
-- XXX Drive directly as parserK rather than converting to parserD first.
-- | Parse a stream using the supplied ParserK 'PRK.Parser'.
--
-- /Internal/
--{-# INLINE parseK #-}
--parseK :: Monad m => PRK.Parser a m b -> Stream m a -> m (Either ParseError b)
--parseK = parse
-- | Parse a stream using the supplied 'Parser'.
--
-- Parsers (See "Streamly.Internal.Data.Parser") are more powerful folds that
-- add backtracking and error functionality to terminating folds. Unlike folds,
-- parsers may not always result in a valid output, they may result in an
-- error. For example:
--
-- >>> Stream.parse (Parser.takeEQ 1 Fold.drain) Stream.nil
-- Left (ParseError "takeEQ: Expecting exactly 1 elements, input terminated on 0")
--
-- Note: @parse p@ is not the same as @head . parseMany p@ on an empty stream.
--
{-# INLINE [3] parse #-}
parse :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b)
parse = parseD
{-# INLINE_NORMAL parseBreakD #-}
parseBreakD :: Monad m =>
PRD.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
parseBreakD parser strm = do
(b, strmD) <- D.parseBreakD parser (toStreamD strm)
return $! (b, fromStreamD strmD)
-- | Parse a stream using the supplied 'Parser'.
--
-- /CPS/
--
--{-# INLINE parseBreak #-}
--parseBreak :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
--parseBreak p strm = D.parseBreak p strm
------------------------------------------------------------------------------
-- Multi-stream folds
------------------------------------------------------------------------------
-- | Returns 'True' if the first stream is the same as or a prefix of the
-- second. A stream is a prefix of itself.
--
-- >>> Stream.isPrefixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char)
-- True
--
{-# INLINE isPrefixOf #-}
isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
-- | Returns 'True' if the first stream is an infix of the second. A stream is
-- considered an infix of itself.
--
-- >>> s = Stream.fromList "hello" :: Stream IO Char
-- >>> Stream.isInfixOf s s
-- True
--
-- Space: @O(n)@ worst case where @n@ is the length of the infix.
--
-- /Pre-release/
--
-- /Requires 'Storable' constraint/
--
{-# INLINE isInfixOf #-}
isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a, Unbox a)
=> Stream m a -> Stream m a -> m Bool
isInfixOf infx stream = do
arr <- fold Array.write infx
-- XXX can use breakOnSeq instead (when available)
r <- D.null $ D.drop 1 $ D.splitOnSeq arr Fold.drain $ toStreamD stream
return (not r)
-- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf
-- may use the suffix stream many times. To run in optimal memory we do not
-- want to buffer the suffix stream in memory therefore we need an ability to
-- clone (or consume it multiple times) the suffix stream without any side
-- effects so that multiple potential suffix matches can proceed in parallel
-- without buffering the suffix stream. For example, we may create the suffix
-- stream from a file handle, however, if we evaluate the stream multiple
-- times, once for each match, we will need a different file handle each time
-- which may exhaust the file descriptors. Instead, we want to share the same
-- underlying file descriptor, use pread on it to generate the stream and clone
-- the stream for each match. Therefore the suffix stream should be built in
-- such a way that it can be consumed multiple times without any problems.
-- XXX Can be implemented with better space/time complexity.
-- Space: @O(n)@ worst case where @n@ is the length of the suffix.
-- | Returns 'True' if the first stream is a suffix of the second. A stream is
-- considered a suffix of itself.
--
-- >>> Stream.isSuffixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char)
-- True
--
-- Space: @O(n)@, buffers entire input stream and the suffix.
--
-- /Pre-release/
--
-- /Suboptimal/ - Help wanted.
--
{-# INLINE isSuffixOf #-}
isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
isSuffixOf suffix stream = reverse suffix `isPrefixOf` reverse stream
-- | Returns 'True' if all the elements of the first stream occur, in order, in
-- the second stream. The elements do not have to occur consecutively. A stream
-- is a subsequence of itself.
--
-- >>> Stream.isSubsequenceOf (Stream.fromList "hlo") (Stream.fromList "hello" :: Stream IO Char)
-- True
--
{-# INLINE isSubsequenceOf #-}
isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)
-- Note: If we want to return a Maybe value to know whether the
-- suffix/infix was present or not along with the stripped stream then
-- we need to buffer the whole input stream.
-- | @stripPrefix prefix input@ strips the @prefix@ stream from the @input@
-- stream if it is a prefix of input. Returns 'Nothing' if the input does not
-- start with the given prefix, stripped input otherwise. Returns @Just nil@
-- when the prefix is the same as the input stream.
--
-- Space: @O(1)@
--
{-# INLINE stripPrefix #-}
stripPrefix
:: (Monad m, Eq a)
=> Stream m a -> Stream m a -> m (Maybe (Stream m a))
stripPrefix m1 m2 = fmap fromStreamD <$>
D.stripPrefix (toStreamD m1) (toStreamD m2)
-- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does
-- not end with the given suffix. Returns @Just nil@ when the suffix is the
-- same as the stream.
--
-- It may be more efficient to convert the stream to an Array and use
-- stripSuffix on that especially if the elements have a Storable or Prim
-- instance.
--
-- See also "Streamly.Internal.Data.Stream.Reduce.dropSuffix".
--
-- Space: @O(n)@, buffers the entire input stream as well as the suffix
--
-- /Pre-release/
{-# INLINE stripSuffix #-}
stripSuffix
:: (Monad m, Eq a)
=> Stream m a -> Stream m a -> m (Maybe (Stream m a))
stripSuffix m1 m2 = fmap reverse <$> stripPrefix (reverse m1) (reverse m2)

View File

@ -1,560 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Enumerate
-- Copyright : (c) 2018 Composewell Technologies
--
-- License : BSD3
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- The functions defined in this module should be rarely needed for direct use,
-- try to use the operations from the 'Enumerable' type class
-- instances instead.
--
-- This module provides an 'Enumerable' type class to enumerate 'Enum' types
-- into a stream. The operations in this type class correspond to similar
-- perations in the 'Enum' type class, the only difference is that they produce
-- a stream instead of a list. These operations cannot be defined generically
-- based on the 'Enum' type class. We provide instances for commonly used
-- types. If instances for other types are needed convenience functions defined
-- in this module can be used to define them. Alternatively, these functions
-- can be used directly.
-- XXX The Unfold.Enumeration module is more modular, check the differences and
-- reconcile the two.
module Streamly.Internal.Data.Stream.Enumerate
(
Enumerable (..)
-- ** Enumerating 'Bounded' 'Enum' Types
, enumerate
, enumerateTo
, enumerateFromBounded
-- ** Enumerating 'Enum' Types not larger than 'Int'
, enumerateFromToSmall
, enumerateFromThenToSmall
, enumerateFromThenSmallBounded
-- ** Enumerating 'Bounded' 'Integral' Types
, enumerateFromIntegral
, enumerateFromThenIntegral
-- ** Enumerating 'Integral' Types
, enumerateFromToIntegral
, enumerateFromThenToIntegral
-- ** Enumerating unbounded 'Integral' Types
, enumerateFromStepIntegral
-- ** Enumerating 'Fractional' Types
, enumerateFromFractional
, enumerateFromToFractional
, enumerateFromThenFractional
, enumerateFromThenToFractional
)
where
import Data.Fixed
import Data.Int
import Data.Ratio
import Data.Word
import Numeric.Natural
import Data.Functor.Identity (Identity(..))
import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD)
import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
-- $setup
-- >>> import Streamly.Data.Fold as Fold
-- >>> import Streamly.Internal.Data.Stream as Stream
-- >>> import Streamly.Internal.Data.Stream.Enumerate as Stream
-------------------------------------------------------------------------------
-- Enumeration of Integral types
-------------------------------------------------------------------------------
--
-- | @enumerateFromStepIntegral from step@ generates an infinite stream whose
-- first element is @from@ and the successive elements are in increments of
-- @step@.
--
-- CAUTION: This function is not safe for finite integral types. It does not
-- check for overflow, underflow or bounds.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)
-- [0,-2,-4]
--
-- @
--
{-# INLINE enumerateFromStepIntegral #-}
enumerateFromStepIntegral
:: (Monad m, Integral a)
=> a -> a -> Stream m a
enumerateFromStepIntegral from stride =
fromStreamD $ D.enumerateFromStepIntegral from stride
-- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a
-- stream whose first element is @from@ and the successive elements are in
-- increments of @1@. The stream is bounded by the size of the 'Integral' type.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)
-- [0,1,2,3]
--
-- @
--
{-# INLINE enumerateFromIntegral #-}
enumerateFromIntegral
:: (Monad m, Integral a, Bounded a)
=> a -> Stream m a
enumerateFromIntegral from = fromStreamD $ D.enumerateFromIntegral from
-- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from
-- then@ generates a stream whose first element is @from@, the second element
-- is @then@ and the successive elements are in increments of @then - from@.
-- The stream is bounded by the size of the 'Integral' type.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)
-- [0,-2,-4,-6]
--
-- @
--
{-# INLINE enumerateFromThenIntegral #-}
enumerateFromThenIntegral
:: (Monad m, Integral a, Bounded a)
=> a -> a -> Stream m a
enumerateFromThenIntegral from next =
fromStreamD $ D.enumerateFromThenIntegral from next
-- | Enumerate an 'Integral' type up to a given limit.
-- @enumerateFromToIntegral from to@ generates a finite stream whose first
-- element is @from@ and successive elements are in increments of @1@ up to
-- @to@.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToIntegral 0 4
-- [0,1,2,3,4]
--
-- @
--
{-# INLINE enumerateFromToIntegral #-}
enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a
enumerateFromToIntegral from to =
fromStreamD $ D.enumerateFromToIntegral from to
-- | Enumerate an 'Integral' type in steps up to a given limit.
-- @enumerateFromThenToIntegral from then to@ generates a finite stream whose
-- first element is @from@, the second element is @then@ and the successive
-- elements are in increments of @then - from@ up to @to@.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 2 6
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)
-- [0,-2,-4,-6]
--
-- @
--
{-# INLINE enumerateFromThenToIntegral #-}
enumerateFromThenToIntegral
:: (Monad m, Integral a)
=> a -> a -> a -> Stream m a
enumerateFromThenToIntegral from next to =
fromStreamD $ D.enumerateFromThenToIntegral from next to
-------------------------------------------------------------------------------
-- Enumeration of Fractional types
-------------------------------------------------------------------------------
--
-- Even though the underlying implementation of enumerateFromFractional and
-- enumerateFromThenFractional works for any 'Num' we have restricted these to
-- 'Fractional' because these do not perform any bounds check, in contrast to
-- integral versions and are therefore not equivalent substitutes for those.
--
-- | Numerically stable enumeration from a 'Fractional' number in steps of size
-- @1@. @enumerateFromFractional from@ generates a stream whose first element
-- is @from@ and the successive elements are in increments of @1@. No overflow
-- or underflow checks are performed.
--
-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1
-- [1.1,2.1,3.1,4.1]
--
-- @
--
--
{-# INLINE enumerateFromFractional #-}
enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a
enumerateFromFractional from = fromStreamD $ D.enumerateFromNum from
-- | Numerically stable enumeration from a 'Fractional' number in steps.
-- @enumerateFromThenFractional from then@ generates a stream whose first
-- element is @from@, the second element is @then@ and the successive elements
-- are in increments of @then - from@. No overflow or underflow checks are
-- performed.
--
-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)
-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
--
-- @
--
{-# INLINE enumerateFromThenFractional #-}
enumerateFromThenFractional
:: (Monad m, Fractional a)
=> a -> a -> Stream m a
enumerateFromThenFractional from next = fromStreamD $ D.enumerateFromThenNum from next
-- | Numerically stable enumeration from a 'Fractional' number to a given
-- limit. @enumerateFromToFractional from to@ generates a finite stream whose
-- first element is @from@ and successive elements are in increments of @1@ up
-- to @to@.
--
-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4.6
-- [1.1,2.1,3.1,4.1,5.1]
--
-- @
--
-- Notice that the last element is equal to the specified @to@ value after
-- rounding to the nearest integer.
--
{-# INLINE enumerateFromToFractional #-}
enumerateFromToFractional
:: (Monad m, Fractional a, Ord a)
=> a -> a -> Stream m a
enumerateFromToFractional from to =
fromStreamD $ D.enumerateFromToFractional from to
-- | Numerically stable enumeration from a 'Fractional' number in steps up to a
-- given limit. @enumerateFromThenToFractional from then to@ generates a
-- finite stream whose first element is @from@, the second element is @then@
-- and the successive elements are in increments of @then - from@ up to @to@.
--
-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 2 6
-- [0.1,2.0,3.9,5.799999999999999]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)
-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
--
-- @
--
--
{-# INLINE enumerateFromThenToFractional #-}
enumerateFromThenToFractional
:: (Monad m, Fractional a, Ord a)
=> a -> a -> a -> Stream m a
enumerateFromThenToFractional from next to =
fromStreamD $ D.enumerateFromThenToFractional from next to
-------------------------------------------------------------------------------
-- Enumeration of Enum types not larger than Int
-------------------------------------------------------------------------------
--
-- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.
--
{-# INLINE enumerateFromToSmall #-}
enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a
enumerateFromToSmall from to =
fmap toEnum
$ enumerateFromToIntegral (fromEnum from) (fromEnum to)
-- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.
--
{-# INLINE enumerateFromThenToSmall #-}
enumerateFromThenToSmall :: (Monad m, Enum a)
=> a -> a -> a -> Stream m a
enumerateFromThenToSmall from next to =
fmap toEnum
$ enumerateFromThenToIntegral
(fromEnum from) (fromEnum next) (fromEnum to)
-- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.
--
-- Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a
-- type is bounded but does not have a 'Bounded' instance then we can go on
-- enumerating it beyond the legal values of the type, resulting in the failure
-- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'
-- instance for this function to be safely used.
--
{-# INLINE enumerateFromThenSmallBounded #-}
enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a)
=> a -> a -> Stream m a
enumerateFromThenSmallBounded from next =
if fromEnum next >= fromEnum from
then enumerateFromThenTo from next maxBound
else enumerateFromThenTo from next minBound
-------------------------------------------------------------------------------
-- Enumerable type class
-------------------------------------------------------------------------------
--
-- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream
-- enumerations like this:
--
-- {-# RULES "fromList enumFrom" [1]
-- forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}
--
-- But this does not work because enumFrom is a class method and GHC rewrites
-- it quickly, so we do not get a chance to have our rule fired.
-- | Types that can be enumerated as a stream. The operations in this type
-- class are equivalent to those in the 'Enum' type class, except that these
-- generate a stream instead of a list. Use the functions in
-- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.
--
class Enum a => Enumerable a where
-- | @enumerateFrom from@ generates a stream starting with the element
-- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
-- generating an infinite stream when the type is not 'Bounded'.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)
-- [0,1,2,3]
--
-- @
--
-- For 'Fractional' types, enumeration is numerically stable. However, no
-- overflow or underflow checks are performed.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1
-- [1.1,2.1,3.1,4.1]
--
-- @
--
enumerateFrom :: (Monad m) => a -> Stream m a
-- | Generate a finite stream starting with the element @from@, enumerating
-- the type up to the value @to@. If @to@ is smaller than @from@ then an
-- empty stream is returned.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 0 4
-- [0,1,2,3,4]
--
-- @
--
-- For 'Fractional' types, the last element is equal to the specified @to@
-- value after rounding to the nearest integral value.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4.6
-- [1.1,2.1,3.1,4.1,5.1]
--
-- @
--
enumerateFromTo :: (Monad m) => a -> a -> Stream m a
-- | @enumerateFromThen from then@ generates a stream whose first element
-- is @from@, the second element is @then@ and the successive elements are
-- in increments of @then - from@. Enumeration can occur downwards or
-- upwards depending on whether @then@ comes before or after @from@. For
-- 'Bounded' types the stream ends when 'maxBound' is reached, for
-- unbounded types it keeps enumerating infinitely.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)
-- [0,-2,-4,-6]
--
-- @
--
enumerateFromThen :: (Monad m) => a -> a -> Stream m a
-- | @enumerateFromThenTo from then to@ generates a finite stream whose
-- first element is @from@, the second element is @then@ and the successive
-- elements are in increments of @then - from@ up to @to@. Enumeration can
-- occur downwards or upwards depending on whether @then@ comes before or
-- after @from@.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 2 6
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)
-- [0,-2,-4,-6]
--
-- @
--
enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a
-- MAYBE: Sometimes it is more convenient to know the count rather then the
-- ending or starting element. For those cases we can define the folllowing
-- APIs. All of these will work only for bounded types if we represent the
-- count by Int.
--
-- enumerateN
-- enumerateFromN
-- enumerateToN
-- enumerateFromStep
-- enumerateFromStepN
-------------------------------------------------------------------------------
-- Convenient functions for bounded types
-------------------------------------------------------------------------------
--
-- |
-- > enumerate = enumerateFrom minBound
--
-- Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'
--
{-# INLINE enumerate #-}
enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a
enumerate = enumerateFrom minBound
-- |
-- > enumerateTo = enumerateFromTo minBound
--
-- Enumerate a 'Bounded' type from its 'minBound' to specified value.
--
{-# INLINE enumerateTo #-}
enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a
enumerateTo = enumerateFromTo minBound
-- |
-- > enumerateFromBounded = enumerateFromTo from maxBound
--
-- 'enumerateFrom' for 'Bounded' 'Enum' types.
--
{-# INLINE enumerateFromBounded #-}
enumerateFromBounded :: (Monad m, Enumerable a, Bounded a)
=> a -> Stream m a
enumerateFromBounded from = enumerateFromTo from maxBound
-------------------------------------------------------------------------------
-- Enumerable Instances
-------------------------------------------------------------------------------
--
-- For Enum types smaller than or equal to Int size.
#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE) \
instance Enumerable SMALL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom = enumerateFromBounded; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen = enumerateFromThenSmallBounded; \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToSmall; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToSmall }
ENUMERABLE_BOUNDED_SMALL(())
ENUMERABLE_BOUNDED_SMALL(Bool)
ENUMERABLE_BOUNDED_SMALL(Ordering)
ENUMERABLE_BOUNDED_SMALL(Char)
-- For bounded Integral Enum types, may be larger than Int.
#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE) \
instance Enumerable INTEGRAL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom = enumerateFromIntegral; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen = enumerateFromThenIntegral; \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToIntegral; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToIntegral }
ENUMERABLE_BOUNDED_INTEGRAL(Int)
ENUMERABLE_BOUNDED_INTEGRAL(Int8)
ENUMERABLE_BOUNDED_INTEGRAL(Int16)
ENUMERABLE_BOUNDED_INTEGRAL(Int32)
ENUMERABLE_BOUNDED_INTEGRAL(Int64)
ENUMERABLE_BOUNDED_INTEGRAL(Word)
ENUMERABLE_BOUNDED_INTEGRAL(Word8)
ENUMERABLE_BOUNDED_INTEGRAL(Word16)
ENUMERABLE_BOUNDED_INTEGRAL(Word32)
ENUMERABLE_BOUNDED_INTEGRAL(Word64)
-- For unbounded Integral Enum types.
#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE) \
instance Enumerable INTEGRAL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom from = enumerateFromStepIntegral from 1; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen from next = \
enumerateFromStepIntegral from (next - from); \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToIntegral; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToIntegral }
ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)
ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)
#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT) \
instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom = enumerateFromFractional; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen = enumerateFromThenFractional; \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToFractional; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToFractional }
ENUMERABLE_FRACTIONAL(Float,)
ENUMERABLE_FRACTIONAL(Double,)
ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)
ENUMERABLE_FRACTIONAL((Ratio a),Integral a)
instance Enumerable a => Enumerable (Identity a) where
{-# INLINE enumerateFrom #-}
enumerateFrom (Identity from) =
fmap Identity $ enumerateFrom from
{-# INLINE enumerateFromThen #-}
enumerateFromThen (Identity from) (Identity next) =
fmap Identity $ enumerateFromThen from next
{-# INLINE enumerateFromTo #-}
enumerateFromTo (Identity from) (Identity to) =
fmap Identity $ enumerateFromTo from to
{-# INLINE enumerateFromThenTo #-}
enumerateFromThenTo (Identity from) (Identity next) (Identity to) =
fmap Identity
$ enumerateFromThenTo from next to
-- TODO
{-
instance Enumerable a => Enumerable (Last a)
instance Enumerable a => Enumerable (First a)
instance Enumerable a => Enumerable (Max a)
instance Enumerable a => Enumerable (Min a)
instance Enumerable a => Enumerable (Const a b)
instance Enumerable (f a) => Enumerable (Alt f a)
instance Enumerable (f a) => Enumerable (Ap f a)
-}

View File

@ -1,222 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Exception
-- Copyright : (c) 2019 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
module Streamly.Internal.Data.Stream.Exception
(
before
, afterUnsafe
, afterIO
, bracketUnsafe
, bracketIO
, bracketIO3
, onException
, finallyUnsafe
, finallyIO
, ghandle
, handle
)
where
import Control.Exception (Exception)
import Control.Monad.Catch (MonadCatch)
import Control.Monad.IO.Class (MonadIO)
import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
import qualified Streamly.Internal.Data.Stream as D
-- $setup
-- >>> :m
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
------------------------------------------------------------------------------
-- Exceptions
------------------------------------------------------------------------------
-- | Run the action @m b@ before the stream yields its first element.
--
-- Same as the following but more efficient due to fusion:
--
-- >>> before action xs = Stream.nilM action <> xs
-- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)
--
{-# INLINE before #-}
before :: Monad m => m b -> Stream m a -> Stream m a
before action xs = fromStreamD $ D.before action $ toStreamD xs
-- | Like 'after', with following differences:
--
-- * action @m b@ won't run if the stream is garbage collected
-- after partial evaluation.
-- * Monad @m@ does not require any other constraints.
-- * has slightly better performance than 'after'.
--
-- Same as the following, but with stream fusion:
--
-- >>> afterUnsafe action xs = xs <> Stream.nilM action
--
-- /Pre-release/
--
{-# INLINE afterUnsafe #-}
afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a
afterUnsafe action xs = fromStreamD $ D.afterUnsafe action $ toStreamD xs
-- | Run the action @IO b@ whenever the stream is evaluated to completion, or
-- if it is garbage collected after a partial lazy evaluation.
--
-- The semantics of the action @IO b@ are similar to the semantics of cleanup
-- action in 'bracketIO'.
--
-- /See also 'afterUnsafe'/
--
{-# INLINE afterIO #-}
afterIO :: MonadIO m => IO b -> Stream m a -> Stream m a
afterIO action xs = fromStreamD $ D.afterIO action $ toStreamD xs
-- | Run the action @m b@ if the stream evaluation is aborted due to an
-- exception. The exception is not caught, simply rethrown.
--
-- /Inhibits stream fusion/
--
{-# INLINE onException #-}
onException :: MonadCatch m => m b -> Stream m a -> Stream m a
onException action xs = fromStreamD $ D.onException action $ toStreamD xs
-- | Like 'finally' with following differences:
--
-- * action @m b@ won't run if the stream is garbage collected
-- after partial evaluation.
-- * has slightly better performance than 'finallyIO'.
--
-- /Inhibits stream fusion/
--
-- /Pre-release/
--
{-# INLINE finallyUnsafe #-}
finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a
finallyUnsafe action xs = fromStreamD $ D.finallyUnsafe action $ toStreamD xs
-- | Run the action @IO b@ whenever the stream stream stops normally, aborts
-- due to an exception or if it is garbage collected after a partial lazy
-- evaluation.
--
-- The semantics of running the action @IO b@ are similar to the cleanup action
-- semantics described in 'bracketIO'.
--
-- >>> finallyIO release = Stream.bracketIO (return ()) (const release)
--
-- /See also 'finallyUnsafe'/
--
-- /Inhibits stream fusion/
--
{-# INLINE finallyIO #-}
finallyIO :: (MonadIO m, MonadCatch m) =>
IO b -> Stream m a -> Stream m a
finallyIO action xs = fromStreamD $ D.finallyIO action $ toStreamD xs
-- | Like 'bracket' but with following differences:
--
-- * alloc action @m b@ runs with async exceptions enabled
-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
-- after partial evaluation.
-- * has slightly better performance than 'bracketIO'.
--
-- /Inhibits stream fusion/
--
-- /Pre-release/
--
{-# INLINE bracketUnsafe #-}
bracketUnsafe :: MonadCatch m
=> m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
bracketUnsafe bef aft bet = fromStreamD $ D.bracketUnsafe bef aft (toStreamD . bet)
-- | Run the alloc action @IO b@ with async exceptions disabled but keeping
-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
--
-- @b@ is usually a resource under the IO monad, e.g. a file handle, that
-- requires a cleanup after use. The cleanup action @b -> IO c@, runs whenever
-- the stream ends normally, due to a sync or async exception or if it gets
-- garbage collected after a partial lazy evaluation.
--
-- 'bracketIO' only guarantees that the cleanup action runs, and it runs with
-- async exceptions enabled. The action must ensure that it can successfully
-- cleanup the resource in the face of sync or async exceptions.
--
-- When the stream ends normally or on a sync exception, cleanup action runs
-- immediately in the current thread context, whereas in other cases it runs in
-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
--
-- /See also: 'bracketUnsafe'/
--
-- /Inhibits stream fusion/
--
{-# INLINE bracketIO #-}
bracketIO :: (MonadIO m, MonadCatch m)
=> IO b -> (b -> IO c) -> (b -> Stream m a) -> Stream m a
bracketIO bef aft = bracketIO3 bef aft aft aft
-- For a use case of this see the "streamly-process" package. It needs to kill
-- the process in case of exception or garbage collection, but waits for the
-- process to terminate in normal cases.
-- | Like 'bracketIO' but can use 3 separate cleanup actions depending on the
-- mode of termination:
--
-- 1. When the stream stops normally
-- 2. When the stream is garbage collected
-- 3. When the stream encounters an exception
--
-- @bracketIO3 before onStop onGC onException action@ runs @action@ using the
-- result of @before@. If the stream stops, @onStop@ action is executed, if the
-- stream is abandoned @onGC@ is executed, if the stream encounters an
-- exception @onException@ is executed.
--
-- /Inhibits stream fusion/
--
-- /Pre-release/
{-# INLINE bracketIO3 #-}
bracketIO3 :: (MonadIO m, MonadCatch m)
=> IO b
-> (b -> IO c)
-> (b -> IO d)
-> (b -> IO e)
-> (b -> Stream m a)
-> Stream m a
bracketIO3 bef aft gc exc bet = fromStreamD $
D.bracketIO3 bef aft exc gc (toStreamD . bet)
-- | Like 'handle' but the exception handler is also provided with the stream
-- that generated the exception as input. The exception handler can thus
-- re-evaluate the stream to retry the action that failed. The exception
-- handler can again call 'ghandle' on it to retry the action multiple times.
--
-- This is highly experimental. In a stream of actions we can map the stream
-- with a retry combinator to retry each action on failure.
--
-- /Inhibits stream fusion/
--
-- /Pre-release/
--
{-# INLINE ghandle #-}
ghandle :: (MonadCatch m, Exception e)
=> (e -> Stream m a -> Stream m a) -> Stream m a -> Stream m a
ghandle handler =
fromStreamD
. D.ghandle (\e xs -> toStreamD $ handler e (fromStreamD xs))
. toStreamD
-- | When evaluating a stream if an exception occurs, stream evaluation aborts
-- and the specified exception handler is run with the exception as argument.
--
-- /Inhibits stream fusion/
--
{-# INLINE handle #-}
handle :: (MonadCatch m, Exception e)
=> (e -> Stream m a) -> Stream m a -> Stream m a
handle handler xs =
fromStreamD $ D.handle (toStreamD . handler) $ toStreamD xs

View File

@ -1,893 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Expand
-- Copyright : (c) 2017 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- Expand a stream by combining two or more streams or by combining streams
-- with unfolds.
module Streamly.Internal.Data.Stream.Expand
(
-- * Binary Combinators (Linear)
-- | Functions ending in the shape:
--
-- @Stream m a -> Stream m a -> Stream m a@.
--
-- The functions in this section have a linear or flat n-ary combining
-- characterstics. It means that when combined @n@ times (e.g. @a `serial`
-- b `serial` c ...@) the resulting expression will have an @O(n)@
-- complexity (instead O(n^2) for pair wise combinators described in the
-- next section. These functions can be used efficiently with
-- 'concatMapWith' et. al. combinators that combine streams in a linear
-- fashion (contrast with 'mergeMapWith' which combines streams as a
-- binary tree).
append
-- * Binary Combinators (Pair Wise)
-- | Like the functions in the section above these functions also combine
-- two streams into a single stream but when used @n@ times linearly they
-- exhibit O(n^2) complexity. They are best combined in a binary tree
-- fashion using 'mergeMapWith' giving a @n * log n@ complexity. Avoid
-- using these with 'concatMapWith' when combining a large or infinite
-- number of streams.
-- ** Append
, append2
-- ** Interleave
, interleave
, interleave2
, interleaveFst
, interleaveFst2
, interleaveFstSuffix2
, interleaveMin
, interleaveMin2
-- ** Round Robin
, roundrobin
-- ** Merge
, mergeBy
, mergeByM
, mergeByM2
, mergeMinBy
, mergeFstBy
-- ** Zip
, zipWith
, zipWithM
-- * Combine Streams and Unfolds
-- |
-- Expand a stream by repeatedly using an unfold and merging the resulting
-- streams. Functions generally ending in the shape:
--
-- @Unfold m a b -> Stream m a -> Stream m b@
-- ** Unfold and combine streams
-- | Unfold and flatten streams.
, unfoldMany -- XXX Rename to unfoldAppend
, unfoldInterleave
, unfoldRoundRobin
-- ** Interpose
-- | Insert effects between streams. Like unfoldMany but intersperses an
-- effect between the streams. A special case of gintercalate.
, interpose
, interposeSuffix
-- , interposeBy
-- ** Intercalate
-- | Insert Streams between Streams.
-- Like unfoldMany but intersperses streams from another source between
-- the streams from the first source.
, intercalate
, intercalateSuffix
, gintercalate
, gintercalateSuffix
-- * Combine Streams of Streams
-- | Map and serially append streams. 'concatMapM' is a generalization of
-- the binary append operation to append many streams.
, concatMapM
, concatMap
, concatEffect
, concat
-- * ConcatMapWith
-- | Map and flatten a stream like 'concatMap' but using a custom binary
-- stream merging combinator instead of just appending the streams. The
-- merging occurs sequentially, it works efficiently for 'serial', 'async',
-- 'ahead' like merge operations where we consume one stream before the
-- next or in case of 'wAsync' or 'parallel' where we consume all streams
-- simultaneously anyway.
--
-- However, in cases where the merging consumes streams in a round robin
-- fashion, a pair wise merging using 'mergeMapWith' would be more
-- efficient. These cases include operations like 'mergeBy' or 'zipWith'.
, concatMapWith
, bindWith
, concatSmapMWith
-- * MergeMapWith
-- | See the notes about suitable merge functions in the 'concatMapWith'
-- section.
, mergeMapWith
-- * Iterate
-- | Map and flatten Trees of Streams
, unfoldIterateDfs
, unfoldIterateBfs
, unfoldIterateBfsRev
, concatIterateWith
, mergeIterateWith
, concatIterateDfs
, concatIterateBfs
-- More experimental ops
, concatIterateBfsRev
, concatIterateLeftsWith
, concatIterateScanWith
, concatIterateScan
)
where
#include "inline.hs"
import Streamly.Internal.Data.Stream.Bottom
( concatEffect, concatMapM, concatMap, smapM, zipWith, zipWithM)
import Streamly.Internal.Data.Stream.Type
( Stream, fromStreamD, fromStreamK, toStreamD, toStreamK
, bindWith, concatMapWith, cons, nil)
import Streamly.Internal.Data.Unfold.Type (Unfold)
import qualified Streamly.Internal.Data.Stream as D
import qualified Streamly.Internal.Data.StreamK as K (mergeBy, mergeByM)
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
import Prelude hiding (concat, concatMap, zipWith)
-- $setup
-- >>> :m
-- >>> import Data.Either (either)
-- >>> import Data.IORef
-- >>> import Streamly.Internal.Data.Stream (Stream)
-- >>> import Prelude hiding (zipWith, concatMap, concat)
-- >>> import qualified Streamly.Data.Array as Array
-- >>> import qualified Streamly.Internal.Data.Fold as Fold
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
-- >>> import qualified Streamly.Internal.Data.Parser as Parser
-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
--
------------------------------------------------------------------------------
-- Appending
------------------------------------------------------------------------------
infixr 6 `append2`
-- | This is fused version of 'append'. It could be up to 100x faster than
-- 'append' when combining two fusible streams. However, it slows down
-- quadratically with the number of streams being appended. Therefore, it is
-- suitable for ad-hoc append of a few streams, and should not be used with
-- 'concatMapWith' or 'mergeMapWith'.
--
-- /Fused/
--
{-# INLINE append2 #-}
append2 ::Monad m => Stream m b -> Stream m b -> Stream m b
append2 m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
infixr 6 `append`
-- | Appends two streams sequentially, yielding all elements from the first
-- stream, and then all elements from the second stream.
--
-- >>> s1 = Stream.fromList [1,2]
-- >>> s2 = Stream.fromList [3,4]
-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
-- [1,2,3,4]
--
-- This has O(n) append performance where @n@ is the number of streams. It can
-- be used to efficiently fold an infinite lazy container of streams
-- 'concatMapWith' et. al.
--
-- See 'append2' for a fusible alternative.
--
-- /CPS/
{-# INLINE append #-}
append :: Stream m a -> Stream m a -> Stream m a
append = (<>)
------------------------------------------------------------------------------
-- Interleaving
------------------------------------------------------------------------------
infixr 6 `interleave`
-- | Interleaves two streams, yielding one element from each stream
-- alternately. When one stream stops the rest of the other stream is used in
-- the output stream.
--
-- When joining many streams in a left associative manner earlier streams will
-- get exponential priority than the ones joining later. Because of exponential
-- weighting it can be used with 'concatMapWith' even on a large number of
-- streams.
--
-- See 'interleave2' for a fusible alternative.
--
-- /CPS/
{-# INLINE interleave #-}
interleave :: Stream m a -> Stream m a -> Stream m a
interleave s1 s2 = fromStreamK $ K.interleave (toStreamK s1) (toStreamK s2)
{-# INLINE interleave2 #-}
interleave2 :: Monad m => Stream m a -> Stream m a -> Stream m a
interleave2 s1 s2 = fromStreamD $ D.interleave (toStreamD s1) (toStreamD s2)
-- | Like `interleave` but stops interleaving as soon as the first stream
-- stops.
--
-- See 'interleaveFst2' for a fusible alternative.
--
-- /CPS/
{-# INLINE interleaveFst #-}
interleaveFst :: Stream m a -> Stream m a -> Stream m a
interleaveFst s1 s2 =
fromStreamK $ K.interleaveFst (toStreamK s1) (toStreamK s2)
-- | Like `interleave` but stops interleaving as soon as any of the two streams
-- stops.
--
-- See 'interleaveMin2' for a fusible alternative.
--
-- /CPS/
{-# INLINE interleaveMin #-}
interleaveMin :: Stream m a -> Stream m a -> Stream m a
interleaveMin s1 s2 =
fromStreamK $ K.interleaveMin (toStreamK s1) (toStreamK s2)
{-# INLINE interleaveMin2 #-}
interleaveMin2 :: Monad m => Stream m a -> Stream m a -> Stream m a
interleaveMin2 s1 s2 =
fromStreamD $ D.interleaveMin (toStreamD s1) (toStreamD s2)
-- | Interleaves the outputs of two streams, yielding elements from each stream
-- alternately, starting from the first stream. As soon as the first stream
-- finishes, the output stops, discarding the remaining part of the second
-- stream. In this case, the last element in the resulting stream would be from
-- the second stream. If the second stream finishes early then the first stream
-- still continues to yield elements until it finishes.
--
-- >>> :set -XOverloadedStrings
-- >>> import Data.Functor.Identity (Identity)
-- >>> Stream.interleaveFstSuffix2 "abc" ",,,," :: Stream Identity Char
-- fromList "a,b,c,"
-- >>> Stream.interleaveFstSuffix2 "abc" "," :: Stream Identity Char
-- fromList "a,bc"
--
-- 'interleaveFstSuffix2' is a dual of 'interleaveFst2'.
--
-- Do not use at scale in concatMapWith.
--
-- /Pre-release/
{-# INLINE interleaveFstSuffix2 #-}
interleaveFstSuffix2 :: Monad m => Stream m b -> Stream m b -> Stream m b
interleaveFstSuffix2 m1 m2 =
fromStreamD $ D.interleaveFstSuffix (toStreamD m1) (toStreamD m2)
-- | Interleaves the outputs of two streams, yielding elements from each stream
-- alternately, starting from the first stream and ending at the first stream.
-- If the second stream is longer than the first, elements from the second
-- stream are infixed with elements from the first stream. If the first stream
-- is longer then it continues yielding elements even after the second stream
-- has finished.
--
-- >>> :set -XOverloadedStrings
-- >>> import Data.Functor.Identity (Identity)
-- >>> Stream.interleaveFst2 "abc" ",,,," :: Stream Identity Char
-- fromList "a,b,c"
-- >>> Stream.interleaveFst2 "abc" "," :: Stream Identity Char
-- fromList "a,bc"
--
-- 'interleaveFst2' is a dual of 'interleaveFstSuffix2'.
--
-- Do not use at scale in concatMapWith.
--
-- /Pre-release/
{-# INLINE interleaveFst2 #-}
interleaveFst2 :: Monad m => Stream m b -> Stream m b -> Stream m b
interleaveFst2 m1 m2 =
fromStreamD $ D.interleaveFst (toStreamD m1) (toStreamD m2)
------------------------------------------------------------------------------
-- Scheduling
------------------------------------------------------------------------------
-- | Schedule the execution of two streams in a fair round-robin manner,
-- executing each stream once, alternately. Execution of a stream may not
-- necessarily result in an output, a stream may chose to @Skip@ producing an
-- element until later giving the other stream a chance to run. Therefore, this
-- combinator fairly interleaves the execution of two streams rather than
-- fairly interleaving the output of the two streams. This can be useful in
-- co-operative multitasking without using explicit threads. This can be used
-- as an alternative to `async`.
--
-- Do not use at scale in concatMapWith.
--
-- /Pre-release/
{-# INLINE roundrobin #-}
roundrobin :: Monad m => Stream m b -> Stream m b -> Stream m b
roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)
------------------------------------------------------------------------------
-- Merging (sorted streams)
------------------------------------------------------------------------------
-- | Merge two streams using a comparison function. The head elements of both
-- the streams are compared and the smaller of the two elements is emitted, if
-- both elements are equal then the element from the first stream is used
-- first.
--
-- If the streams are sorted in ascending order, the resulting stream would
-- also remain sorted in ascending order.
--
-- >>> s1 = Stream.fromList [1,3,5]
-- >>> s2 = Stream.fromList [2,4,6,8]
-- >>> Stream.fold Fold.toList $ Stream.mergeBy compare s1 s2
-- [1,2,3,4,5,6,8]
--
-- See 'mergeByM2' for a fusible alternative.
--
-- /CPS/
{-# INLINE mergeBy #-}
mergeBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
mergeBy f m1 m2 = fromStreamK $ K.mergeBy f (toStreamK m1) (toStreamK m2)
-- | Like 'mergeBy' but with a monadic comparison function.
--
-- Merge two streams randomly:
--
-- @
-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
-- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])
-- [2,1,2,2,2,1,1,1]
-- @
--
-- Merge two streams in a proportion of 2:1:
--
-- >>> :{
-- do
-- let s1 = Stream.fromList [1,1,1,1,1,1]
-- s2 = Stream.fromList [2,2,2]
-- let proportionately m n = do
-- ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]
-- return $ \_ _ -> do
-- r <- readIORef ref
-- writeIORef ref $ Prelude.tail r
-- return $ Prelude.head r
-- f <- proportionately 2 1
-- xs <- Stream.fold Fold.toList $ Stream.mergeByM f s1 s2
-- print xs
-- :}
-- [1,1,2,1,1,2,1,1,2]
--
-- See 'mergeByM2' for a fusible alternative.
--
-- /CPS/
{-# INLINE mergeByM #-}
mergeByM
:: Monad m
=> (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
mergeByM f m1 m2 = fromStreamK $ K.mergeByM f (toStreamK m1) (toStreamK m2)
-- | Like 'mergeByM' but much faster, works best when merging statically known
-- number of streams. When merging more than two streams try to merge pairs and
-- pair of pairs in a tree like structure.'mergeByM' works better with variable
-- number of streams being merged using 'mergeMapWith'.
--
-- /Internal/
{-# INLINE mergeByM2 #-}
mergeByM2
:: Monad m
=> (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
mergeByM2 f m1 m2 =
fromStreamD $ D.mergeByM f (toStreamD m1) (toStreamD m2)
-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
--
-- /Unimplemented/
{-# INLINABLE mergeMinBy #-}
mergeMinBy :: -- Monad m =>
(a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
mergeMinBy _f _m1 _m2 = undefined
-- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)
-- | Like 'mergeByM' but stops merging as soon as the first stream stops.
--
-- /Unimplemented/
{-# INLINABLE mergeFstBy #-}
mergeFstBy :: -- Monad m =>
(a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
mergeFstBy _f _m1 _m2 = undefined
-- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
------------------------------------------------------------------------------
-- Combine N Streams - unfoldMany
------------------------------------------------------------------------------
-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
-- therefore provide many times better performance.
--
{-# INLINE unfoldMany #-}
unfoldMany ::Monad m => Unfold m a b -> Stream m a -> Stream m b
unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
-- | This does not pair streams like mergeMapWith, instead, it goes through
-- each stream one by one and yields one element from each stream. After it
-- goes to the last stream it reverses the traversal to come back to the first
-- stream yielding elements from each stream on its way back to the first
-- stream and so on.
--
-- >>> lists = Stream.fromList [[1,1],[2,2],[3,3],[4,4],[5,5]]
-- >>> interleaved = Stream.unfoldInterleave Unfold.fromList lists
-- >>> Stream.fold Fold.toList interleaved
-- [1,2,3,4,5,5,4,3,2,1]
--
-- Note that this is order of magnitude more efficient than "mergeMapWith
-- wSerial" because of fusion.
--
-- /Fused/
{-# INLINE unfoldInterleave #-}
unfoldInterleave ::Monad m => Unfold m a b -> Stream m a -> Stream m b
unfoldInterleave u m =
fromStreamD $ D.unfoldInterleave u (toStreamD m)
-- | 'unfoldInterleave' switches to the next stream whenever a value from a
-- stream is yielded, it does not switch on a 'Skip'. So if a stream keeps
-- skipping for long time other streams won't get a chance to run.
-- 'unfoldRoundRobin' switches on Skip as well. So it basically schedules each
-- stream fairly irrespective of whether it produces a value or not.
--
{-# INLINE unfoldRoundRobin #-}
unfoldRoundRobin ::Monad m => Unfold m a b -> Stream m a -> Stream m b
unfoldRoundRobin u m =
fromStreamD $ D.unfoldRoundRobin u (toStreamD m)
------------------------------------------------------------------------------
-- Combine N Streams - interpose
------------------------------------------------------------------------------
-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
-- | Unfold the elements of a stream, intersperse the given element between the
-- unfolded streams and then concat them into a single stream.
--
-- >>> unwords = Stream.interpose ' '
--
-- /Pre-release/
{-# INLINE interpose #-}
interpose :: Monad m
=> c -> Unfold m b c -> Stream m b -> Stream m c
interpose x unf str =
fromStreamD $ D.interpose x unf (toStreamD str)
-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
-- | Unfold the elements of a stream, append the given element after each
-- unfolded stream and then concat them into a single stream.
--
-- >>> unlines = Stream.interposeSuffix '\n'
--
-- /Pre-release/
{-# INLINE interposeSuffix #-}
interposeSuffix :: Monad m
=> c -> Unfold m b c -> Stream m b -> Stream m c
interposeSuffix x unf str =
fromStreamD $ D.interposeSuffix x unf (toStreamD str)
------------------------------------------------------------------------------
-- Combine N Streams - intercalate
------------------------------------------------------------------------------
-- XXX we can swap the order of arguments to gintercalate so that the
-- definition of unfoldMany becomes simpler? The first stream should be
-- infixed inside the second one. However, if we change the order in
-- "interleave" as well similarly, then that will make it a bit unintuitive.
--
-- > unfoldMany unf str =
-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
-- | 'interleaveFst' followed by unfold and concat.
--
-- /Pre-release/
{-# INLINE gintercalate #-}
gintercalate
:: Monad m
=> Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
gintercalate unf1 str1 unf2 str2 =
fromStreamD $ D.gintercalate
unf1 (toStreamD str1)
unf2 (toStreamD str2)
-- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)
-- | 'intersperse' followed by unfold and concat.
--
-- >>> intercalate u a = Stream.unfoldMany u . Stream.intersperse a
-- >>> intersperse = Stream.intercalate Unfold.identity
-- >>> unwords = Stream.intercalate Unfold.fromList " "
--
-- >>> input = Stream.fromList ["abc", "def", "ghi"]
-- >>> Stream.fold Fold.toList $ Stream.intercalate Unfold.fromList " " input
-- "abc def ghi"
--
{-# INLINE intercalate #-}
intercalate :: Monad m
=> Unfold m b c -> b -> Stream m b -> Stream m c
intercalate unf seed str = fromStreamD $
D.unfoldMany unf $ D.intersperse seed (toStreamD str)
-- | 'interleaveFstSuffix2' followed by unfold and concat.
--
-- /Pre-release/
{-# INLINE gintercalateSuffix #-}
gintercalateSuffix
:: Monad m
=> Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
gintercalateSuffix unf1 str1 unf2 str2 =
fromStreamD $ D.gintercalateSuffix
unf1 (toStreamD str1)
unf2 (toStreamD str2)
-- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)
-- | 'intersperseMSuffix' followed by unfold and concat.
--
-- >>> intercalateSuffix u a = Stream.unfoldMany u . Stream.intersperseMSuffix a
-- >>> intersperseMSuffix = Stream.intercalateSuffix Unfold.identity
-- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"
--
-- >>> input = Stream.fromList ["abc", "def", "ghi"]
-- >>> Stream.fold Fold.toList $ Stream.intercalateSuffix Unfold.fromList "\n" input
-- "abc\ndef\nghi\n"
--
{-# INLINE intercalateSuffix #-}
intercalateSuffix :: Monad m
=> Unfold m b c -> b -> Stream m b -> Stream m c
intercalateSuffix unf seed =
fromStreamD . D.intercalateSuffix unf seed . toStreamD
------------------------------------------------------------------------------
-- Combine N Streams - concatMap
------------------------------------------------------------------------------
-- | Flatten a stream of streams to a single stream.
--
-- >>> concat = Stream.concatMap id
--
-- /Pre-release/
{-# INLINE concat #-}
concat :: Monad m => Stream m (Stream m a) -> Stream m a
concat = concatMap id
------------------------------------------------------------------------------
-- Combine N Streams - concatMap
------------------------------------------------------------------------------
-- | Like 'concatMapWith' but carries a state which can be used to share
-- information across multiple steps of concat.
--
-- >>> concatSmapMWith combine f initial = Stream.concatMapWith combine id . Stream.smapM f initial
--
-- /Pre-release/
--
{-# INLINE concatSmapMWith #-}
concatSmapMWith
:: Monad m
=> (Stream m b -> Stream m b -> Stream m b)
-> (s -> a -> m (s, Stream m b))
-> m s
-> Stream m a
-> Stream m b
concatSmapMWith combine f initial =
concatMapWith combine id . smapM f initial
-- XXX Implement a StreamD version for fusion.
-- | Combine streams in pairs using a binary combinator, the resulting streams
-- are then combined again in pairs recursively until we get to a single
-- combined stream. The composition would thus form a binary tree.
--
-- For example, you can sort a stream using merge sort like this:
--
-- >>> s = Stream.fromList [5,1,7,9,2]
-- >>> generate = Stream.fromPure
-- >>> combine = Stream.mergeBy compare
-- >>> Stream.fold Fold.toList $ Stream.mergeMapWith combine generate s
-- [1,2,5,7,9]
--
-- Note that if the stream length is not a power of 2, the binary tree composed
-- by mergeMapWith would not be balanced, which may or may not be important
-- depending on what you are trying to achieve.
--
-- /Caution: the stream of streams must be finite/
--
-- /CPS/
--
-- /Pre-release/
--
{-# INLINE mergeMapWith #-}
mergeMapWith ::
(Stream m b -> Stream m b -> Stream m b)
-> (a -> Stream m b)
-> Stream m a
-> Stream m b
mergeMapWith par f m =
fromStreamK
$ K.mergeMapWith
(\s1 s2 -> toStreamK $ fromStreamK s1 `par` fromStreamK s2)
(toStreamK . f)
(toStreamK m)
------------------------------------------------------------------------------
-- concatIterate - Map and flatten Trees of Streams
------------------------------------------------------------------------------
-- | Yield an input element in the output stream, map a stream generator on it
-- and repeat the process on the resulting stream. Resulting streams are
-- flattened using the 'concatMapWith' combinator. This can be used for a depth
-- first style (DFS) traversal of a tree like structure.
--
-- Example, list a directory tree using DFS:
--
-- >>> f = either Dir.readEitherPaths (const Stream.nil)
-- >>> input = Stream.fromPure (Left ".")
-- >>> ls = Stream.concatIterateWith Stream.append f input
--
-- Note that 'iterateM' is a special case of 'concatIterateWith':
--
-- >>> iterateM f = Stream.concatIterateWith Stream.append (Stream.fromEffect . f) . Stream.fromEffect
--
-- /CPS/
--
-- /Pre-release/
--
{-# INLINE concatIterateWith #-}
concatIterateWith ::
(Stream m a -> Stream m a -> Stream m a)
-> (a -> Stream m a)
-> Stream m a
-> Stream m a
concatIterateWith combine f = iterateStream
where
iterateStream = concatMapWith combine generate
generate x = x `cons` iterateStream (f x)
-- | Traverse the stream in depth first style (DFS). Map each element in the
-- input stream to a stream and flatten, recursively map the resulting elements
-- as well to a stream and flatten until no more streams are generated.
--
-- Example, list a directory tree using DFS:
--
-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)
-- >>> input = Stream.fromPure (Left ".")
-- >>> ls = Stream.concatIterateDfs f input
--
-- This is equivalent to using @concatIterateWith Stream.append@.
--
-- /Pre-release/
{-# INLINE concatIterateDfs #-}
concatIterateDfs :: Monad m =>
(a -> Maybe (Stream m a))
-> Stream m a
-> Stream m a
concatIterateDfs f stream =
fromStreamD
$ D.concatIterateDfs (fmap toStreamD . f ) (toStreamD stream)
-- | Similar to 'concatIterateDfs' except that it traverses the stream in
-- breadth first style (BFS). First, all the elements in the input stream are
-- emitted, and then their traversals are emitted.
--
-- Example, list a directory tree using BFS:
--
-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)
-- >>> input = Stream.fromPure (Left ".")
-- >>> ls = Stream.concatIterateBfs f input
--
-- /Pre-release/
{-# INLINE concatIterateBfs #-}
concatIterateBfs :: Monad m =>
(a -> Maybe (Stream m a))
-> Stream m a
-> Stream m a
concatIterateBfs f stream =
fromStreamD
$ D.concatIterateBfs (fmap toStreamD . f ) (toStreamD stream)
-- | Same as 'concatIterateBfs' except that the traversal of the last
-- element on a level is emitted first and then going backwards up to the first
-- element (reversed ordering). This may be slightly faster than
-- 'concatIterateBfs'.
--
{-# INLINE concatIterateBfsRev #-}
concatIterateBfsRev :: Monad m =>
(a -> Maybe (Stream m a))
-> Stream m a
-> Stream m a
concatIterateBfsRev f stream =
fromStreamD
$ D.concatIterateBfsRev (fmap toStreamD . f ) (toStreamD stream)
-- | Like 'concatIterateWith' but uses the pairwise flattening combinator
-- 'mergeMapWith' for flattening the resulting streams. This can be used for a
-- balanced traversal of a tree like structure.
--
-- Example, list a directory tree using balanced traversal:
--
-- >>> f = either Dir.readEitherPaths (const Stream.nil)
-- >>> input = Stream.fromPure (Left ".")
-- >>> ls = Stream.mergeIterateWith Stream.interleave f input
--
-- /CPS/
--
-- /Pre-release/
--
{-# INLINE mergeIterateWith #-}
mergeIterateWith ::
(Stream m a -> Stream m a -> Stream m a)
-> (a -> Stream m a)
-> Stream m a
-> Stream m a
mergeIterateWith combine f = iterateStream
where
iterateStream = mergeMapWith combine generate
generate x = x `cons` iterateStream (f x)
-- | Same as @concatIterateDfs@ but more efficient due to stream fusion.
--
-- Example, list a directory tree using DFS:
--
-- >>> f = Unfold.either Dir.eitherReaderPaths Unfold.nil
-- >>> input = Stream.fromPure (Left ".")
-- >>> ls = Stream.unfoldIterateDfs f input
--
-- /Pre-release/
{-# INLINE unfoldIterateDfs #-}
unfoldIterateDfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a
unfoldIterateDfs u = fromStreamD . D.unfoldIterateDfs u . toStreamD
-- | Like 'unfoldIterateDfs' but uses breadth first style traversal.
--
-- /Pre-release/
{-# INLINE unfoldIterateBfs #-}
unfoldIterateBfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a
unfoldIterateBfs u = fromStreamD . D.unfoldIterateBfs u . toStreamD
-- | Like 'unfoldIterateBfs' but processes the children in reverse order,
-- therefore, may be slightly faster.
--
-- /Pre-release/
{-# INLINE unfoldIterateBfsRev #-}
unfoldIterateBfsRev :: Monad m => Unfold m a a -> Stream m a -> Stream m a
unfoldIterateBfsRev u =
fromStreamD . D.unfoldIterateBfsRev u . toStreamD
------------------------------------------------------------------------------
-- Flattening Graphs
------------------------------------------------------------------------------
-- To traverse graphs we need a state to be carried around in the traversal.
-- For example, we can use a hashmap to store the visited status of nodes.
-- | Like 'iterateMap' but carries a state in the stream generation function.
-- This can be used to traverse graph like structures, we can remember the
-- visited nodes in the state to avoid cycles.
--
-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
-- traverse graphs. However, this function provides a more localized state
-- instead of using a global state.
--
-- See also: 'mfix'
--
-- /Pre-release/
--
{-# INLINE concatIterateScanWith #-}
concatIterateScanWith
:: Monad m
=> (Stream m a -> Stream m a -> Stream m a)
-> (b -> a -> m (b, Stream m a))
-> m b
-> Stream m a
-> Stream m a
concatIterateScanWith combine f initial stream =
concatEffect $ do
b <- initial
iterateStream (b, stream)
where
iterateStream (b, s) = pure $ concatMapWith combine (generate b) s
generate b a = a `cons` feedback b a
feedback b a = concatEffect $ f b a >>= iterateStream
-- Next stream is to be generated by the return value of the previous stream. A
-- general intuitive way of doing that could be to use an appending monad
-- instance for streams where the result of the previous stream is used to
-- generate the next one. In the first pass we can just emit the values in the
-- stream and keep building a buffered list/stream, once done we can then
-- process the buffered stream.
{-# INLINE concatIterateScan #-}
concatIterateScan
:: Monad m
=> (b -> a -> m b)
-> (b -> m (Maybe (b, Stream m a)))
-> b
-> Stream m a
concatIterateScan scanner generate initial =
fromStreamD
$ D.concatIterateScan
scanner (fmap (fmap (fmap toStreamD)) . generate) initial
------------------------------------------------------------------------------
-- Either streams
------------------------------------------------------------------------------
-- Keep concating either streams as long as rights are generated, stop as soon
-- as a left is generated and concat the left stream.
--
-- See also: 'handle'
--
-- /Unimplemented/
--
{-
concatMapEitherWith
:: (forall x. t m x -> t m x -> t m x)
-> (a -> t m (Either (Stream m b) b))
-> Stream m a
-> Stream m b
concatMapEitherWith = undefined
-}
-- XXX We should prefer using the Maybe stream returning signatures over this.
-- This API should perhaps be removed in favor of those.
-- | In an 'Either' stream iterate on 'Left's. This is a special case of
-- 'concatIterateWith':
--
-- >>> concatIterateLeftsWith combine f = Stream.concatIterateWith combine (either f (const Stream.nil))
--
-- To traverse a directory tree:
--
-- >>> input = Stream.fromPure (Left ".")
-- >>> ls = Stream.concatIterateLeftsWith Stream.append Dir.readEither input
--
-- /Pre-release/
--
{-# INLINE concatIterateLeftsWith #-}
concatIterateLeftsWith
:: (b ~ Either a c)
=> (Stream m b -> Stream m b -> Stream m b)
-> (a -> Stream m b)
-> Stream m b
-> Stream m b
concatIterateLeftsWith combine f =
concatIterateWith combine (either f (const nil))

View File

@ -1,460 +0,0 @@
{-# OPTIONS_GHC -Wno-orphans #-}
-- |
-- Module : Streamly.Internal.Data.Stream.Generate
-- Copyright : (c) 2017 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
module Streamly.Internal.Data.Stream.Generate
(
-- * Primitives
Stream.nil
, Stream.nilM
, Stream.cons
, Stream.consM
-- * From 'Unfold'
, unfold
-- * Unfolding
, unfoldr
, unfoldrM
-- * From Values
, Stream.fromPure
, Stream.fromEffect
, repeat
, repeatM
, replicate
, replicateM
-- * Enumeration
, Enumerable (..)
, enumerate
, enumerateTo
-- * Time Enumeration
, times
, timesWith
, absTimes
, absTimesWith
, relTimes
, relTimesWith
, durations
, timeout
-- * Iteration
, iterate
, iterateM
-- * Cyclic Elements
, mfix
-- * From Containers
, Bottom.fromList
, fromFoldable
-- * From memory
, fromPtr
, fromPtrN
, fromByteStr#
-- , fromByteArray#
, fromUnboxedIORef
)
where
#include "inline.hs"
import Control.Monad.IO.Class (MonadIO)
import Data.Word (Word8)
import Foreign.Storable (Storable)
import GHC.Exts (Addr#, Ptr (Ptr))
import Streamly.Internal.Data.Stream.Bottom
(absTimesWith, relTimesWith, timesWith)
import Streamly.Internal.Data.Stream.Enumerate
(Enumerable(..), enumerate, enumerateTo)
import Streamly.Internal.Data.Stream.Type
(Stream, fromStreamD, fromStreamK, toStreamK)
import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
import Streamly.Internal.Data.Unbox (Unbox)
import Streamly.Internal.Data.Unfold.Type (Unfold)
import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed
import qualified Streamly.Internal.Data.Stream.Bottom as Bottom
import qualified Streamly.Internal.Data.Stream as D
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
import qualified Streamly.Internal.Data.Stream.Type as Stream
import qualified Streamly.Internal.Data.Stream.Transform as Stream (sequence)
import Prelude hiding (iterate, replicate, repeat, take)
-- $setup
-- >>> :m
-- >>> import Control.Concurrent (threadDelay)
-- >>> import Data.Function (fix, (&))
-- >>> import Data.Semigroup (cycle1)
-- >>> import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Unfold as Unfold
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
-- >>> import GHC.Exts (Ptr (Ptr))
------------------------------------------------------------------------------
-- From Unfold
------------------------------------------------------------------------------
-- | Convert an 'Unfold' into a stream by supplying it an input seed.
--
-- >>> s = Stream.unfold Unfold.replicateM (3, putStrLn "hello")
-- >>> Stream.fold Fold.drain s
-- hello
-- hello
-- hello
--
{-# INLINE unfold #-}
unfold :: Monad m => Unfold m a b -> a -> Stream m b
unfold unf = Stream.fromStreamD . D.unfold unf
------------------------------------------------------------------------------
-- Generation by Unfolding
------------------------------------------------------------------------------
-- |
-- >>> :{
-- unfoldr step s =
-- case step s of
-- Nothing -> Stream.nil
-- Just (a, b) -> a `Stream.cons` unfoldr step b
-- :}
--
-- Build a stream by unfolding a /pure/ step function @step@ starting from a
-- seed @s@. The step function returns the next element in the stream and the
-- next seed value. When it is done it returns 'Nothing' and the stream ends.
-- For example,
--
-- >>> :{
-- let f b =
-- if b > 2
-- then Nothing
-- else Just (b, b + 1)
-- in Stream.fold Fold.toList $ Stream.unfoldr f 0
-- :}
-- [0,1,2]
--
{-# INLINE_EARLY unfoldr #-}
unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a
unfoldr step seed = fromStreamD (D.unfoldr step seed)
{-# RULES "unfoldr fallback to StreamK" [1]
forall a b. D.toStreamK (D.unfoldr a b) = K.unfoldr a b #-}
-- | Build a stream by unfolding a /monadic/ step function starting from a
-- seed. The step function returns the next element in the stream and the next
-- seed value. When it is done it returns 'Nothing' and the stream ends. For
-- example,
--
-- >>> :{
-- let f b =
-- if b > 2
-- then return Nothing
-- else return (Just (b, b + 1))
-- in Stream.fold Fold.toList $ Stream.unfoldrM f 0
-- :}
-- [0,1,2]
--
{-# INLINE unfoldrM #-}
unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> Stream m a
unfoldrM step = fromStreamD . D.unfoldrM step
------------------------------------------------------------------------------
-- From Values
------------------------------------------------------------------------------
-- |
-- Generate an infinite stream by repeating a pure value.
--
{-# INLINE_NORMAL repeat #-}
repeat :: Monad m => a -> Stream m a
repeat = fromStreamD . D.repeat
-- |
-- >>> repeatM = Stream.sequence . Stream.repeat
-- >>> repeatM = fix . Stream.consM
-- >>> repeatM = cycle1 . Stream.fromEffect
--
-- Generate a stream by repeatedly executing a monadic action forever.
--
-- >>> :{
-- repeatAction =
-- Stream.repeatM (threadDelay 1000000 >> print 1)
-- & Stream.take 10
-- & Stream.fold Fold.drain
-- :}
--
{-# INLINE_NORMAL repeatM #-}
repeatM :: Monad m => m a -> Stream m a
repeatM = Stream.sequence . repeat
-- |
-- >>> replicate n = Stream.take n . Stream.repeat
--
-- Generate a stream of length @n@ by repeating a value @n@ times.
--
{-# INLINE_NORMAL replicate #-}
replicate :: Monad m => Int -> a -> Stream m a
replicate n = fromStreamD . D.replicate n
-- |
-- >>> replicateM n = Stream.sequence . Stream.replicate n
--
-- Generate a stream by performing a monadic action @n@ times.
{-# INLINE_NORMAL replicateM #-}
replicateM :: Monad m => Int -> m a -> Stream m a
replicateM n = Stream.sequence . replicate n
------------------------------------------------------------------------------
-- Time Enumeration
------------------------------------------------------------------------------
-- | @times@ returns a stream of time value tuples with clock of 10 ms
-- granularity. The first component of the tuple is an absolute time reference
-- (epoch) denoting the start of the stream and the second component is a time
-- relative to the reference.
--
-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)
-- >>> Stream.fold f $ Stream.take 3 $ Stream.times
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Pre-release/
--
{-# INLINE times #-}
times :: MonadIO m => Stream m (AbsTime, RelTime64)
times = timesWith 0.01
-- | @absTimes@ returns a stream of absolute timestamps using a clock of 10 ms
-- granularity.
--
-- >>> f = Fold.drainMapM print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Pre-release/
--
{-# INLINE absTimes #-}
absTimes :: MonadIO m => Stream m AbsTime
absTimes = fmap (uncurry addToAbsTime64) times
-- | @relTimes@ returns a stream of relative time values starting from 0,
-- using a clock of granularity 10 ms.
--
-- >>> f = Fold.drainMapM print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Pre-release/
--
{-# INLINE relTimes #-}
relTimes :: MonadIO m => Stream m RelTime64
relTimes = fmap snd times
-- | @durations g@ returns a stream of relative time values measuring the time
-- elapsed since the immediate predecessor element of the stream was generated.
-- The first element of the stream is always 0. @durations@ uses a clock of
-- granularity @g@ specified in seconds. A low granularity clock is more
-- expensive in terms of CPU usage. The minimum granularity is 1 millisecond.
-- Durations lower than 1 ms will be 0.
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Unimplemented/
--
{-# INLINE durations #-}
durations :: -- Monad m =>
Double -> t m RelTime64
durations = undefined
-- | Generate a singleton event at or after the specified absolute time. Note
-- that this is different from a threadDelay, a threadDelay starts from the
-- time when the action is evaluated, whereas if we use AbsTime based timeout
-- it will immediately expire if the action is evaluated too late.
--
-- /Unimplemented/
--
{-# INLINE timeout #-}
timeout :: -- Monad m =>
AbsTime -> t m ()
timeout = undefined
------------------------------------------------------------------------------
-- Iterating functions
------------------------------------------------------------------------------
-- |
-- >>> iterate f x = x `Stream.cons` iterate f x
--
-- Generate an infinite stream with @x@ as the first element and each
-- successive element derived by applying the function @f@ on the previous
-- element.
--
-- >>> Stream.fold Fold.toList $ Stream.take 5 $ Stream.iterate (+1) 1
-- [1,2,3,4,5]
--
{-# INLINE_NORMAL iterate #-}
iterate :: Monad m => (a -> a) -> a -> Stream m a
iterate step = fromStreamD . D.iterate step
-- |
-- >>> iterateM f m = m >>= \a -> return a `Stream.consM` iterateM f (f a)
--
-- Generate an infinite stream with the first element generated by the action
-- @m@ and each successive element derived by applying the monadic function
-- @f@ on the previous element.
--
-- >>> :{
-- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)
-- & Stream.take 3
-- & Stream.fold Fold.toList
-- :}
-- 0
-- 1
-- [0,1,2]
--
{-# INLINE iterateM #-}
iterateM :: Monad m => (a -> m a) -> m a -> Stream m a
iterateM step = fromStreamD . D.iterateM step
-- | We can define cyclic structures using @let@:
--
-- >>> let (a, b) = ([1, b], head a) in (a, b)
-- ([1,1],1)
--
-- The function @fix@ defined as:
--
-- >>> fix f = let x = f x in x
--
-- ensures that the argument of a function and its output refer to the same
-- lazy value @x@ i.e. the same location in memory. Thus @x@ can be defined
-- in terms of itself, creating structures with cyclic references.
--
-- >>> f ~(a, b) = ([1, b], head a)
-- >>> fix f
-- ([1,1],1)
--
-- 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic
-- values.
--
-- Using 'mfix' for streams we can construct a stream in which each element of
-- the stream is defined in a cyclic fashion. The argument of the function
-- being fixed represents the current element of the stream which is being
-- returned by the stream monad. Thus, we can use the argument to construct
-- itself.
--
-- In the following example, the argument @action@ of the function @f@
-- represents the tuple @(x,y)@ returned by it in a given iteration. We define
-- the first element of the tuple in terms of the second.
--
-- >>> import Streamly.Internal.Data.Stream as Stream
-- >>> import System.IO.Unsafe (unsafeInterleaveIO)
--
-- >>> :{
-- main = Stream.fold (Fold.drainMapM print) $ Stream.mfix f
-- where
-- f action = unCrossStream $ do
-- let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act
-- x <- CrossStream (Stream.sequence $ Stream.fromList [incr 1 action, incr 2 action])
-- y <- CrossStream (Stream.fromList [4,5])
-- return (x, y)
-- :}
--
-- Note: you cannot achieve this by just changing the order of the monad
-- statements because that would change the order in which the stream elements
-- are generated.
--
-- Note that the function @f@ must be lazy in its argument, that's why we use
-- 'unsafeInterleaveIO' on @action@ because IO monad is strict.
--
-- /CPS/
--
-- /Pre-release/
{-# INLINE mfix #-}
mfix :: Monad m => (m a -> Stream m a) -> Stream m a
mfix f = fromStreamK $ K.mfix (toStreamK . f)
------------------------------------------------------------------------------
-- Conversions
------------------------------------------------------------------------------
-- |
-- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil
--
-- Construct a stream from a 'Foldable' containing pure values:
--
-- /CPS/
--
{-# INLINE fromFoldable #-}
fromFoldable :: Foldable f => f a -> Stream m a
fromFoldable = fromStreamK . K.fromFoldable
------------------------------------------------------------------------------
-- From pointers
------------------------------------------------------------------------------
-- | Keep reading 'Storable' elements from 'Ptr' onwards.
--
-- /Unsafe:/ The caller is responsible for safe addressing.
--
-- /Pre-release/
{-# INLINE fromPtr #-}
fromPtr :: (MonadIO m, Storable a) => Ptr a -> Stream m a
fromPtr = Stream.fromStreamD . D.fromPtr
-- | Take @n@ 'Storable' elements starting from 'Ptr' onwards.
--
-- >>> fromPtrN n = Stream.take n . Stream.fromPtr
--
-- /Unsafe:/ The caller is responsible for safe addressing.
--
-- /Pre-release/
{-# INLINE fromPtrN #-}
fromPtrN :: (MonadIO m, Storable a) => Int -> Ptr a -> Stream m a
fromPtrN n = Stream.fromStreamD . D.take n . D.fromPtr
-- | Read bytes from an 'Addr#' until a 0 byte is encountered, the 0 byte is
-- not included in the stream.
--
-- >>> fromByteStr# addr = Stream.takeWhile (/= 0) $ Stream.fromPtr $ Ptr addr
--
-- /Unsafe:/ The caller is responsible for safe addressing.
--
-- Note that this is completely safe when reading from Haskell string
-- literals because they are guaranteed to be NULL terminated:
--
-- >>> Stream.fold Fold.toList $ Stream.fromByteStr# "\1\2\3\0"#
-- [1,2,3]
--
{-# INLINE fromByteStr# #-}
fromByteStr# :: MonadIO m => Addr# -> Stream m Word8
fromByteStr# addr =
Stream.fromStreamD $ D.takeWhile (/= 0) $ D.fromPtr $ Ptr addr
-- | Construct a stream by reading an 'Unboxed' 'IORef' repeatedly.
--
-- /Pre-release/
--
{-# INLINE fromUnboxedIORef #-}
fromUnboxedIORef :: (MonadIO m, Unbox a) => Unboxed.IORef a -> Stream m a
fromUnboxedIORef = fromStreamD . Unboxed.toStreamD

View File

@ -1,95 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Lift
-- Copyright : (c) 2019 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
module Streamly.Internal.Data.Stream.Lift
(
-- * Generalize Inner Monad
morphInner
, generalizeInner
-- * Transform Inner Monad
, liftInnerWith
, runInnerWith
, runInnerWithState
)
where
import Data.Functor.Identity (Identity (..))
import Streamly.Internal.Data.Stream.Type
(Stream, fromStreamD, toStreamD, fromStreamK, toStreamK)
import qualified Streamly.Internal.Data.Stream as D
import qualified Streamly.Internal.Data.StreamK as K
-- $setup
-- >>> :m
-- >>> import Data.Functor.Identity (runIdentity)
-- >>> import Streamly.Internal.Data.Stream as Stream
------------------------------------------------------------------------------
-- Generalize the underlying monad
------------------------------------------------------------------------------
-- | Transform the inner monad of a stream using a natural transformation.
--
-- Example, generalize the inner monad from Identity to any other:
--
-- >>> generalizeInner = Stream.morphInner (return . runIdentity)
--
-- Also known as hoist.
--
-- /CPS/
{-# INLINE morphInner #-}
morphInner :: (Monad m, Monad n)
=> (forall x. m x -> n x) -> Stream m a -> Stream n a
morphInner f xs = fromStreamK $ K.hoist f (toStreamK xs)
-- | Generalize the inner monad of the stream from 'Identity' to any monad.
--
-- Definition:
--
-- >>> generalizeInner = Stream.morphInner (return . runIdentity)
--
-- /CPS/
--
{-# INLINE generalizeInner #-}
generalizeInner :: Monad m => Stream Identity a -> Stream m a
generalizeInner = morphInner (return . runIdentity)
-- fromStreamK $ K.hoist (return . runIdentity) (toStreamK xs)
------------------------------------------------------------------------------
-- Add and remove a monad transformer
------------------------------------------------------------------------------
-- | Lift the inner monad @m@ of a stream @Stream m a@ to @t m@ using the
-- supplied lift function.
--
{-# INLINE liftInnerWith #-}
liftInnerWith :: (Monad m, Monad (t m))
=> (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a
liftInnerWith lift xs = fromStreamD $ D.liftInnerWith lift (toStreamD xs)
-- | Evaluate the inner monad of a stream using the supplied runner function.
--
{-# INLINE runInnerWith #-}
runInnerWith :: (Monad m, Applicative (t m)) =>
(forall b. t m b -> m b) -> Stream (t m) a -> Stream m a
runInnerWith run xs = fromStreamD $ D.runInnerWith run (toStreamD xs)
-- | Evaluate the inner monad of a stream using the supplied stateful runner
-- function and the initial state. The state returned by an invocation of the
-- runner is supplied as input state to the next invocation.
--
{-# INLINE runInnerWithState #-}
runInnerWithState :: (Monad m, Applicative (t m)) =>
(forall b. s -> t m b -> m (b, s))
-> m s
-> Stream (t m) a
-> Stream m (s, a)
runInnerWithState run initial xs =
fromStreamD $ D.runInnerWithState run initial (toStreamD xs)

View File

@ -1,444 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Reduce
-- Copyright : (c) 2017 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- Reduce streams by streams, folds or parsers.
module Streamly.Internal.Data.Stream.Reduce
(
-- * Reduce By Streams
dropPrefix
, dropInfix
, dropSuffix
-- * Reduce By Folds
-- |
-- Reduce a stream by folding or parsing chunks of the stream. Functions
-- generally ending in these shapes:
--
-- @
-- f (Fold m a b) -> Stream m a -> Stream m b
-- f (Parser a m b) -> Stream m a -> Stream m b
-- @
-- ** Generic Folding
-- | Apply folds on a stream.
, foldMany
, foldManyPost
, refoldMany
, foldSequence
, foldIterateM
, refoldIterateM
, reduceIterateBfs
-- ** Chunking
-- | Element unaware grouping.
, chunksOf
-- ** Splitting
-- XXX Implement these as folds or parsers instead.
, splitOnSuffixSeqAny
, splitOnPrefix
, splitOnAny
-- * Reduce By Parsers
-- ** Generic Parsing
-- | Apply parsers on a stream.
, parseMany
, parseManyD
, parseManyTill
, parseSequence
, parseIterate
)
where
import Control.Monad.IO.Class (MonadIO(..))
import Streamly.Internal.Data.Array.Type (Array)
import Streamly.Internal.Data.Fold.Type (Fold (..))
import Streamly.Internal.Data.Parser (Parser (..))
import Streamly.Internal.Data.Parser (ParseError)
import Streamly.Internal.Data.Refold.Type (Refold (..))
import Streamly.Internal.Data.Stream.Bottom (foldManyPost)
import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
import Streamly.Internal.Data.Unbox (Unbox)
import qualified Streamly.Internal.Data.Array.Type as Array
import qualified Streamly.Internal.Data.Parser as ParserD
import qualified Streamly.Internal.Data.Stream as D
import Prelude hiding (concatMap, map)
-- $setup
-- >>> :m
-- >>> import Prelude hiding (zipWith, concatMap, concat)
-- >>> import Streamly.Internal.Data.Stream as Stream
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Internal.Data.Fold as Fold
-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
-- >>> import qualified Streamly.Internal.Data.Parser as Parser
-- >>> import qualified Streamly.Data.Array as Array
------------------------------------------------------------------------------
-- Trimming
------------------------------------------------------------------------------
-- | Drop prefix from the input stream if present.
--
-- Space: @O(1)@
--
-- /Unimplemented/
{-# INLINE dropPrefix #-}
dropPrefix ::
-- (Monad m, Eq a) =>
Stream m a -> Stream m a -> Stream m a
dropPrefix = error "Not implemented yet!"
-- | Drop all matching infix from the input stream if present. Infix stream
-- may be consumed multiple times.
--
-- Space: @O(n)@ where n is the length of the infix.
--
-- /Unimplemented/
{-# INLINE dropInfix #-}
dropInfix ::
-- (Monad m, Eq a) =>
Stream m a -> Stream m a -> Stream m a
dropInfix = error "Not implemented yet!"
-- | Drop suffix from the input stream if present. Suffix stream may be
-- consumed multiple times.
--
-- Space: @O(n)@ where n is the length of the suffix.
--
-- /Unimplemented/
{-# INLINE dropSuffix #-}
dropSuffix ::
-- (Monad m, Eq a) =>
Stream m a -> Stream m a -> Stream m a
dropSuffix = error "Not implemented yet!"
------------------------------------------------------------------------------
-- Folding
------------------------------------------------------------------------------
-- | Apply a 'Fold' repeatedly on a stream and emit the results in the
-- output stream. Unlike 'foldManyPost' it evaluates the fold after the stream,
-- therefore, an empty input stream results in an empty output stream.
--
-- Definition:
--
-- >>> foldMany f = Stream.parseMany (Parser.fromFold f)
--
-- Example, empty stream:
--
-- >>> f = Fold.take 2 Fold.sum
-- >>> fmany = Stream.fold Fold.toList . Stream.foldMany f
-- >>> fmany $ Stream.fromList []
-- []
--
-- Example, last fold empty:
--
-- >>> fmany $ Stream.fromList [1..4]
-- [3,7]
--
-- Example, last fold non-empty:
--
-- >>> fmany $ Stream.fromList [1..5]
-- [3,7,5]
--
-- Note that using a closed fold e.g. @Fold.take 0@, would result in an
-- infinite stream on a non-empty input stream.
--
{-# INLINE foldMany #-}
foldMany
:: Monad m
=> Fold m a b
-> Stream m a
-> Stream m b
foldMany f m = fromStreamD $ D.foldMany f (toStreamD m)
-- | Like 'foldMany' but using the 'Refold' type instead of 'Fold'.
--
-- /Pre-release/
{-# INLINE refoldMany #-}
refoldMany :: Monad m =>
Refold m c a b -> m c -> Stream m a -> Stream m b
refoldMany f action = fromStreamD . D.refoldMany f action . toStreamD
-- | Apply a stream of folds to an input stream and emit the results in the
-- output stream.
--
-- /Unimplemented/
--
{-# INLINE foldSequence #-}
foldSequence
:: -- Monad m =>
Stream m (Fold m a b)
-> Stream m a
-> Stream m b
foldSequence _f _m = undefined
-- | Iterate a fold generator on a stream. The initial value @b@ is used to
-- generate the first fold, the fold is applied on the stream and the result of
-- the fold is used to generate the next fold and so on.
--
-- >>> import Data.Monoid (Sum(..))
-- >>> f x = return (Fold.take 2 (Fold.sconcat x))
-- >>> s = fmap Sum $ Stream.fromList [1..10]
-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s
-- [3,10,21,36,55,55]
--
-- This is the streaming equivalent of monad like sequenced application of
-- folds where next fold is dependent on the previous fold.
--
-- /Pre-release/
--
{-# INLINE foldIterateM #-}
foldIterateM ::
Monad m => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b
foldIterateM f i m = fromStreamD $ D.foldIterateM f i (toStreamD m)
-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be
-- much more efficient due to stream fusion.
--
-- /Internal/
{-# INLINE refoldIterateM #-}
refoldIterateM :: Monad m =>
Refold m b a b -> m b -> Stream m a -> Stream m b
refoldIterateM c i m = fromStreamD $ D.refoldIterateM c i (toStreamD m)
-- | Binary BFS style reduce, folds a level entirely using the supplied fold
-- function, collecting the outputs as next level of the tree, then repeats the
-- same process on the next level. The last elements of a previously folded
-- level are folded first.
{-# INLINE reduceIterateBfs #-}
reduceIterateBfs :: Monad m =>
(a -> a -> m a) -> Stream m a -> m (Maybe a)
reduceIterateBfs f stream = D.reduceIterateBfs f (toStreamD stream)
------------------------------------------------------------------------------
-- Splitting
------------------------------------------------------------------------------
-- Implement this as a fold or a parser instead.
-- This can be implemented easily using Rabin Karp
-- | Split post any one of the given patterns.
--
-- /Unimplemented/
{-# INLINE splitOnSuffixSeqAny #-}
splitOnSuffixSeqAny :: -- (Monad m, Unboxed a, Integral a) =>
[Array a] -> Fold m a b -> Stream m a -> Stream m b
splitOnSuffixSeqAny _subseq _f _m = undefined
-- D.fromStreamD $ D.splitPostAny f subseq (D.toStreamD m)
-- | Split on a prefixed separator element, dropping the separator. The
-- supplied 'Fold' is applied on the split segments.
--
-- @
-- > splitOnPrefix' p xs = Stream.toList $ Stream.splitOnPrefix p (Fold.toList) (Stream.fromList xs)
-- > splitOnPrefix' (== '.') ".a.b"
-- ["a","b"]
-- @
--
-- An empty stream results in an empty output stream:
-- @
-- > splitOnPrefix' (== '.') ""
-- []
-- @
--
-- An empty segment consisting of only a prefix is folded to the default output
-- of the fold:
--
-- @
-- > splitOnPrefix' (== '.') "."
-- [""]
--
-- > splitOnPrefix' (== '.') ".a.b."
-- ["a","b",""]
--
-- > splitOnPrefix' (== '.') ".a..b"
-- ["a","","b"]
--
-- @
--
-- A prefix is optional at the beginning of the stream:
--
-- @
-- > splitOnPrefix' (== '.') "a"
-- ["a"]
--
-- > splitOnPrefix' (== '.') "a.b"
-- ["a","b"]
-- @
--
-- 'splitOnPrefix' is an inverse of 'intercalatePrefix' with a single element:
--
-- > Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList . Stream.splitOnPrefix (== '.') Fold.toList === id
--
-- Assuming the input stream does not contain the separator:
--
-- > Stream.splitOnPrefix (== '.') Fold.toList . Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList === id
--
-- /Unimplemented/
{-# INLINE splitOnPrefix #-}
splitOnPrefix :: -- (IsStream t, MonadCatch m) =>
(a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
splitOnPrefix _predicate _f = undefined
-- parseMany (Parser.sliceBeginBy predicate f)
-- Int list examples for splitOn:
--
-- >>> splitList [] [1,2,3,3,4]
-- > [[1],[2],[3],[3],[4]]
--
-- >>> splitList [5] [1,2,3,3,4]
-- > [[1,2,3,3,4]]
--
-- >>> splitList [1] [1,2,3,3,4]
-- > [[],[2,3,3,4]]
--
-- >>> splitList [4] [1,2,3,3,4]
-- > [[1,2,3,3],[]]
--
-- >>> splitList [2] [1,2,3,3,4]
-- > [[1],[3,3,4]]
--
-- >>> splitList [3] [1,2,3,3,4]
-- > [[1,2],[],[4]]
--
-- >>> splitList [3,3] [1,2,3,3,4]
-- > [[1,2],[4]]
--
-- >>> splitList [1,2,3,3,4] [1,2,3,3,4]
-- > [[],[]]
-- This can be implemented easily using Rabin Karp
-- | Split on any one of the given patterns.
--
-- /Unimplemented/
--
{-# INLINE splitOnAny #-}
splitOnAny :: -- (Monad m, Unboxed a, Integral a) =>
[Array a] -> Fold m a b -> Stream m a -> Stream m b
splitOnAny _subseq _f _m =
undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.toStreamD m)
------------------------------------------------------------------------------
-- Parsing
------------------------------------------------------------------------------
-- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the
-- output stream.
--
-- Example:
--
-- >>> s = Stream.fromList [1..10]
-- >>> parser = Parser.takeBetween 0 2 Fold.sum
-- >>> Stream.fold Fold.toList $ Stream.parseMany parser s
-- [Right 3,Right 7,Right 11,Right 15,Right 19]
--
-- This is the streaming equivalent of the 'Streamly.Data.Parser.many' parse
-- combinator.
--
-- Known Issues: When the parser fails there is no way to get the remaining
-- stream.
--
{-# INLINE parseMany #-}
parseMany
:: Monad m
=> Parser a m b
-> Stream m a
-> Stream m (Either ParseError b)
parseMany p m =
fromStreamD $ D.parseManyD p (toStreamD m)
-- | Same as parseMany but for StreamD streams.
--
-- /Internal/
--
{-# INLINE parseManyD #-}
parseManyD
:: Monad m
=> ParserD.Parser a m b
-> Stream m a
-> Stream m (Either ParseError b)
parseManyD p m =
fromStreamD $ D.parseManyD p (toStreamD m)
-- | Apply a stream of parsers to an input stream and emit the results in the
-- output stream.
--
-- /Unimplemented/
--
{-# INLINE parseSequence #-}
parseSequence
:: -- Monad m =>
Stream m (Parser a m b)
-> Stream m a
-> Stream m b
parseSequence _f _m = undefined
-- XXX Change the parser arguments' order
-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
-- output of @test@ is discarded and the output of @collect@ is emitted in the
-- output stream. The parser fails if @collect@ fails.
--
-- /Unimplemented/
--
{-# INLINE parseManyTill #-}
parseManyTill ::
-- MonadThrow m =>
Parser a m b
-> Parser a m x
-> t m a
-> t m b
parseManyTill = undefined
-- | Iterate a parser generating function on a stream. The initial value @b@ is
-- used to generate the first parser, the parser is applied on the stream and
-- the result is used to generate the next parser and so on.
--
-- >>> import Data.Monoid (Sum(..))
-- >>> s = Stream.fromList [1..10]
-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.catRights $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) (Sum 0) $ fmap Sum s
-- [3,10,21,36,55,55]
--
-- This is the streaming equivalent of monad like sequenced application of
-- parsers where next parser is dependent on the previous parser.
--
-- /Pre-release/
--
{-# INLINE parseIterate #-}
parseIterate
:: Monad m
=> (b -> Parser a m b)
-> b
-> Stream m a
-> Stream m (Either ParseError b)
parseIterate f i m = fromStreamD $
D.parseIterateD f i (toStreamD m)
------------------------------------------------------------------------------
-- Chunking
------------------------------------------------------------------------------
-- | @chunksOf n stream@ groups the elements in the input stream into arrays of
-- @n@ elements each.
--
-- Same as the following but may be more efficient:
--
-- >>> chunksOf n = Stream.foldMany (Array.writeN n)
--
-- /Pre-release/
{-# INLINE chunksOf #-}
chunksOf :: (MonadIO m, Unbox a)
=> Int -> Stream m a -> Stream m (Array a)
chunksOf n = fromStreamD . Array.chunksOf n . toStreamD

View File

@ -820,7 +820,7 @@ isSuffixOfUnbox suffix stream =
-- stripSuffix on that especially if the elements have a Storable or Prim
-- instance.
--
-- See also "Streamly.Internal.Data.Stream.Reduce.dropSuffix".
-- See also "Streamly.Internal.Data.Stream.StreamD.Reduce.dropSuffix".
--
-- Space: @O(n)@, buffers the entire input stream as well as the suffix
--

View File

@ -1,52 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.StreamDK
-- Copyright : (c) 2019 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
--
-- This module has the following problems due to rewrite rules:
--
-- * Rewrite rules lead to optimization problems, blocking fusion in some
-- cases, specifically when combining multiple operations e.g. (filter . drop).
-- * Rewrite rules lead to problems when calling a function recursively. For
-- example, the StreamD version of foldBreak cannot be used recursively when
-- wrapped in rewrite rules because each recursive call adds a roundtrip
-- conversion from D to K and back to D. We can use the StreamK versions of
-- these though because the rewrite rule gets eliminated in that case.
-- * If we have a unified module, we need two different versions of several
-- operations e.g. appendK and appendD, both are useful in different cases.
--
module Streamly.Internal.Data.Stream.StreamDK
( module Streamly.Internal.Data.Stream.Type
, module Streamly.Internal.Data.Stream.Bottom
, module Streamly.Internal.Data.Stream.Eliminate
, module Streamly.Internal.Data.Stream.Exception
, module Streamly.Internal.Data.Stream.Expand
, module Streamly.Internal.Data.Stream.Generate
, module Streamly.Internal.Data.Stream.Lift
, module Streamly.Internal.Data.Stream.Reduce
, module Streamly.Internal.Data.Stream.Transform
, module Streamly.Internal.Data.Stream.Cross
, module Streamly.Internal.Data.Stream.Zip
-- modules having dependencies on libraries other than base
, module Streamly.Internal.Data.Stream.Transformer
)
where
import Streamly.Internal.Data.Stream.Bottom
import Streamly.Internal.Data.Stream.Cross
import Streamly.Internal.Data.Stream.Eliminate
import Streamly.Internal.Data.Stream.Exception
import Streamly.Internal.Data.Stream.Expand
import Streamly.Internal.Data.Stream.Generate
import Streamly.Internal.Data.Stream.Lift
import Streamly.Internal.Data.Stream.Reduce
import Streamly.Internal.Data.Stream.Transform
import Streamly.Internal.Data.Stream.Type
import Streamly.Internal.Data.Stream.Zip
import Streamly.Internal.Data.Stream.Transformer

File diff suppressed because it is too large Load Diff

View File

@ -1,135 +0,0 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Transformer
-- Copyright : (c) 2019 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
module Streamly.Internal.Data.Stream.Transformer
(
foldlT
, foldrT
, liftInner
, usingReaderT
, runReaderT
, evalStateT
, usingStateT
, runStateT
)
where
import Control.Monad.Trans.Class (MonadTrans)
import Control.Monad.Trans.Reader (ReaderT)
import Control.Monad.Trans.State.Strict (StateT)
import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
import qualified Streamly.Internal.Data.Stream.StreamD.Transformer as D
-- $setup
-- >>> :m
-- >>> import Control.Monad.Trans.Class (lift)
-- >>> import Control.Monad.Trans.Identity (runIdentityT)
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
-- | Lazy left fold to a transformer monad.
--
{-# INLINE foldlT #-}
foldlT :: (Monad m, Monad (s m), MonadTrans s)
=> (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
foldlT f z s = D.foldlT f z (toStreamD s)
-- | Right fold to a transformer monad. This is the most general right fold
-- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'
-- implementation can be more efficient:
--
-- >>> foldrS = Stream.foldrT
--
-- >>> step f x xs = lift $ f x (runIdentityT xs)
-- >>> foldrM f z s = runIdentityT $ Stream.foldrT (step f) (lift z) s
--
-- 'foldrT' can be used to translate streamly streams to other transformer
-- monads e.g. to a different streaming type.
--
-- /Pre-release/
{-# INLINE foldrT #-}
foldrT :: (Monad m, Monad (s m), MonadTrans s)
=> (a -> s m b -> s m b) -> s m b -> Stream m a -> s m b
foldrT f z s = D.foldrT f z (toStreamD s)
------------------------------------------------------------------------------
-- Add and remove a monad transformer
------------------------------------------------------------------------------
-- | Lift the inner monad @m@ of @Stream m a@ to @t m@ where @t@ is a monad
-- transformer.
--
{-# INLINE liftInner #-}
liftInner :: (Monad m, MonadTrans t, Monad (t m))
=> Stream m a -> Stream (t m) a
liftInner xs = fromStreamD $ D.liftInner (toStreamD xs)
------------------------------------------------------------------------------
-- Sharing read only state in a stream
------------------------------------------------------------------------------
-- | Evaluate the inner monad of a stream as 'ReaderT'.
--
{-# INLINE runReaderT #-}
runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a
runReaderT s xs = fromStreamD $ D.runReaderT s (toStreamD xs)
-- | Run a stream transformation using a given environment.
--
-- See also: 'Serial.map'
--
-- / Internal/
--
{-# INLINE usingReaderT #-}
usingReaderT
:: Monad m
=> m r
-> (Stream (ReaderT r m) a -> Stream (ReaderT r m) a)
-> Stream m a
-> Stream m a
usingReaderT r f xs = runReaderT r $ f $ liftInner xs
------------------------------------------------------------------------------
-- Sharing read write state in a stream
------------------------------------------------------------------------------
-- | Evaluate the inner monad of a stream as 'StateT'.
--
-- >>> evalStateT s = fmap snd . Stream.runStateT s
--
-- / Internal/
--
{-# INLINE evalStateT #-}
evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a
-- evalStateT s = fmap snd . runStateT s
evalStateT s xs = fromStreamD $ D.evalStateT s (toStreamD xs)
-- | Run a stateful (StateT) stream transformation using a given state.
--
-- >>> usingStateT s f = Stream.evalStateT s . f . Stream.liftInner
--
-- See also: 'scan'
--
-- / Internal/
--
{-# INLINE usingStateT #-}
usingStateT
:: Monad m
=> m s
-> (Stream (StateT s m) a -> Stream (StateT s m) a)
-> Stream m a
-> Stream m a
usingStateT s f = evalStateT s . f . liftInner
-- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting
-- state and value pair after each step.
--
{-# INLINE runStateT #-}
runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)
runStateT s xs = fromStreamD $ D.runStateT s (toStreamD xs)

View File

@ -1,491 +0,0 @@
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Streamly.Internal.Data.Stream.Type
-- Copyright : (c) 2017 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
module Streamly.Internal.Data.Stream.Type
(
-- * Stream Type
Stream -- XXX To be removed
, StreamK
-- * Type Conversion
, fromStreamK
, toStreamK
, fromStreamD
, toStreamD
, fromStream
, toStream
, Streamly.Internal.Data.Stream.Type.fromList
-- * Construction
, cons
, consM
, nil
, nilM
, fromPure
, fromEffect
-- * Applicative
, crossApply
, crossApplySnd
, crossApplyFst
, crossWith
, cross
-- * Bind/Concat
, bindWith
, concatMapWith
-- * Double folds
, eqBy
, cmpBy
)
where
#include "inline.hs"
import Control.Applicative (liftA2)
import Data.Foldable (Foldable(foldl'), fold)
import Data.Functor.Identity (Identity(..), runIdentity)
import Data.Maybe (fromMaybe)
import Data.Semigroup (Endo(..))
import GHC.Exts (IsList(..), IsString(..), oneShot)
import Streamly.Internal.BaseCompat ((#.))
import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
import Text.Read
( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
, readListPrecDefault)
import qualified Streamly.Internal.Data.Stream.Common as P
import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
-- $setup
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
------------------------------------------------------------------------------
-- Stream
------------------------------------------------------------------------------
-- | Semigroup instance appends two streams:
--
-- >>> (<>) = Stream.append
--
newtype StreamK m a = StreamK (K.StreamK m a)
-- XXX when deriving do we inherit an INLINE?
deriving (Semigroup, Monoid)
type Stream = StreamK
------------------------------------------------------------------------------
-- Conversions
------------------------------------------------------------------------------
{-# INLINE_EARLY fromStreamK #-}
fromStreamK :: K.StreamK m a -> Stream m a
fromStreamK = StreamK
{-# INLINE_EARLY toStreamK #-}
toStreamK :: Stream m a -> K.StreamK m a
toStreamK (StreamK k) = k
{-# INLINE_EARLY fromStreamD #-}
fromStreamD :: Monad m => D.Stream m a -> Stream m a
fromStreamD = fromStreamK . D.toStreamK
{-# INLINE_EARLY toStreamD #-}
toStreamD :: Applicative m => Stream m a -> D.Stream m a
toStreamD = D.fromStreamK . toStreamK
{-# INLINE fromStream #-}
fromStream :: Monad m => D.Stream m a -> Stream m a
fromStream = fromStreamD
{-# INLINE toStream #-}
toStream :: Applicative m => Stream m a -> D.Stream m a
toStream = toStreamD
------------------------------------------------------------------------------
-- Generation
------------------------------------------------------------------------------
-- |
-- >>> fromList = Prelude.foldr Stream.cons Stream.nil
--
-- Construct a stream from a list of pure values. This is more efficient than
-- 'fromFoldable'.
--
{-# INLINE fromList #-}
fromList :: Monad m => [a] -> Stream m a
fromList = fromStreamK . P.fromList
------------------------------------------------------------------------------
-- Comparison
------------------------------------------------------------------------------
-- | Compare two streams for equality
--
{-# INLINE eqBy #-}
eqBy :: Monad m =>
(a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
eqBy f m1 m2 = D.eqBy f (toStreamD m1) (toStreamD m2)
-- | Compare two streams
--
{-# INLINE cmpBy #-}
cmpBy
:: Monad m
=> (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
cmpBy f m1 m2 = D.cmpBy f (toStreamD m1) (toStreamD m2)
------------------------------------------------------------------------------
-- Functor
------------------------------------------------------------------------------
instance Monad m => Functor (Stream m) where
{-# INLINE fmap #-}
-- IMPORTANT: do not use eta reduction.
fmap f m = fromStreamD $ D.mapM (return . f) $ toStreamD m
{-# INLINE (<$) #-}
(<$) = fmap . const
------------------------------------------------------------------------------
-- Lists
------------------------------------------------------------------------------
-- Serial streams can act like regular lists using the Identity monad
-- XXX Show instance is 10x slower compared to read, we can do much better.
-- The list show instance itself is really slow.
-- XXX The default definitions of "<" in the Ord instance etc. do not perform
-- well, because they do not get inlined. Need to add INLINE in Ord class in
-- base?
instance IsList (Stream Identity a) where
type (Item (Stream Identity a)) = a
{-# INLINE fromList #-}
fromList xs = StreamK $ P.fromList xs
{-# INLINE toList #-}
toList (StreamK xs) = runIdentity $ P.toList xs
instance Eq a => Eq (Stream Identity a) where
{-# INLINE (==) #-}
(==) (StreamK xs) (StreamK ys) = runIdentity $ P.eqBy (==) xs ys
instance Ord a => Ord (Stream Identity a) where
{-# INLINE compare #-}
compare (StreamK xs) (StreamK ys) = runIdentity $ P.cmpBy compare xs ys
{-# INLINE (<) #-}
x < y =
case compare x y of
LT -> True
_ -> False
{-# INLINE (<=) #-}
x <= y =
case compare x y of
GT -> False
_ -> True
{-# INLINE (>) #-}
x > y =
case compare x y of
GT -> True
_ -> False
{-# INLINE (>=) #-}
x >= y =
case compare x y of
LT -> False
_ -> True
{-# INLINE max #-}
max x y = if x <= y then y else x
{-# INLINE min #-}
min x y = if x <= y then x else y
instance Show a => Show (Stream Identity a) where
showsPrec p dl = showParen (p > 10) $
showString "fromList " . shows (toList dl)
instance Read a => Read (Stream Identity a) where
readPrec = parens $ prec 10 $ do
Ident "fromList" <- lexP
Streamly.Internal.Data.Stream.Type.fromList <$> readPrec
readListPrec = readListPrecDefault
instance (a ~ Char) => IsString (Stream Identity a) where
{-# INLINE fromString #-}
fromString xs = StreamK $ P.fromList xs
-------------------------------------------------------------------------------
-- Foldable
-------------------------------------------------------------------------------
-- The default Foldable instance has several issues:
-- 1) several definitions do not have INLINE on them, so we provide
-- re-implementations with INLINE pragmas.
-- 2) the definitions of sum/product/maximum/minimum are inefficient as they
-- use right folds, they cannot run in constant memory. We provide
-- implementations using strict left folds here.
instance (Foldable m, Monad m) => Foldable (Stream m) where
{-# INLINE foldMap #-}
foldMap f (StreamK xs) = fold $ P.foldr (mappend . f) mempty xs
{-# INLINE foldr #-}
foldr f z t = appEndo (foldMap (Endo #. f) t) z
{-# INLINE foldl' #-}
foldl' f z0 xs = foldr f' id xs z0
where f' x k = oneShot $ \z -> k $! f z x
{-# INLINE length #-}
length = foldl' (\n _ -> n + 1) 0
{-# INLINE elem #-}
elem = any . (==)
{-# INLINE maximum #-}
maximum =
fromMaybe (errorWithoutStackTrace "maximum: empty stream")
. toMaybe
. foldl' getMax Nothing'
where
getMax Nothing' x = Just' x
getMax (Just' mx) x = Just' $! max mx x
{-# INLINE minimum #-}
minimum =
fromMaybe (errorWithoutStackTrace "minimum: empty stream")
. toMaybe
. foldl' getMin Nothing'
where
getMin Nothing' x = Just' x
getMin (Just' mn) x = Just' $! min mn x
{-# INLINE sum #-}
sum = foldl' (+) 0
{-# INLINE product #-}
product = foldl' (*) 1
-------------------------------------------------------------------------------
-- Traversable
-------------------------------------------------------------------------------
instance Traversable (Stream Identity) where
{-# INLINE traverse #-}
traverse f (StreamK xs) =
fmap StreamK $ runIdentity $ P.foldr consA (pure mempty) xs
where
consA x ys = liftA2 K.cons (f x) ys
-------------------------------------------------------------------------------
-- Construction
-------------------------------------------------------------------------------
infixr 5 `cons`
-- | A right associative prepend operation to add a pure value at the head of
-- an existing stream::
--
-- >>> s = 1 `Stream.cons` 2 `Stream.cons` 3 `Stream.cons` Stream.nil
-- >>> Stream.fold Fold.toList s
-- [1,2,3]
--
-- It can be used efficiently with 'Prelude.foldr':
--
-- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil
--
-- Same as the following but more efficient:
--
-- >>> cons x xs = return x `Stream.consM` xs
--
-- /CPS/
--
{-# INLINE_NORMAL cons #-}
cons :: a -> Stream m a -> Stream m a
cons x = fromStreamK . K.cons x . toStreamK
infixr 5 `consM`
-- | A right associative prepend operation to add an effectful value at the
-- head of an existing stream::
--
-- >>> s = putStrLn "hello" `consM` putStrLn "world" `consM` Stream.nil
-- >>> Stream.fold Fold.drain s
-- hello
-- world
--
-- It can be used efficiently with 'Prelude.foldr':
--
-- >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil
--
-- Same as the following but more efficient:
--
-- >>> consM x xs = Stream.fromEffect x `Stream.append` xs
--
-- /CPS/
--
{-# INLINE consM #-}
{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
consM :: Monad m => m a -> Stream m a -> Stream m a
consM m = fromStreamK . K.consM m . toStreamK
-- | A stream that terminates without producing any output or side effect.
--
-- >>> Stream.fold Fold.toList Stream.nil
-- []
--
{-# INLINE_NORMAL nil #-}
nil :: Stream m a
nil = fromStreamK K.nil
-- | A stream that terminates without producing any output, but produces a side
-- effect.
--
-- >>> Stream.fold Fold.toList (Stream.nilM (print "nil"))
-- "nil"
-- []
--
-- /Pre-release/
{-# INLINE_NORMAL nilM #-}
nilM :: Monad m => m b -> Stream m a
nilM = fromStreamK . K.nilM
-- | Create a singleton stream from a pure value.
--
-- >>> fromPure a = a `cons` Stream.nil
-- >>> fromPure = pure
-- >>> fromPure = fromEffect . pure
--
{-# INLINE_NORMAL fromPure #-}
fromPure :: a -> Stream m a
fromPure = fromStreamK . K.fromPure
-- | Create a singleton stream from a monadic action.
--
-- >>> fromEffect m = m `consM` Stream.nil
-- >>> fromEffect = Stream.sequence . Stream.fromPure
--
-- >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")
-- hello
--
{-# INLINE_NORMAL fromEffect #-}
fromEffect :: Monad m => m a -> Stream m a
fromEffect = fromStreamK . K.fromEffect
-------------------------------------------------------------------------------
-- Applicative
-------------------------------------------------------------------------------
-- | Apply a stream of functions to a stream of values and flatten the results.
--
-- Note that the second stream is evaluated multiple times.
--
-- >>> crossApply = Stream.crossWith id
--
{-# INLINE crossApply #-}
crossApply :: Stream m (a -> b) -> Stream m a -> Stream m b
crossApply m1 m2 =
fromStreamK $ K.crossApply (toStreamK m1) (toStreamK m2)
{-# INLINE crossApplySnd #-}
crossApplySnd :: Stream m a -> Stream m b -> Stream m b
crossApplySnd m1 m2 =
fromStreamK $ K.crossApplySnd (toStreamK m1) (toStreamK m2)
{-# INLINE crossApplyFst #-}
crossApplyFst :: Stream m a -> Stream m b -> Stream m a
crossApplyFst m1 m2 =
fromStreamK $ K.crossApplyFst (toStreamK m1) (toStreamK m2)
-- |
-- Definition:
--
-- >>> crossWith f m1 m2 = fmap f m1 `Stream.crossApply` m2
--
-- Note that the second stream is evaluated multiple times.
--
{-# INLINE crossWith #-}
crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
crossWith f m1 m2 = fmap f m1 `crossApply` m2
-- | Given a @Stream m a@ and @Stream m b@ generate a stream with all possible
-- combinations of the tuple @(a, b)@.
--
-- Definition:
--
-- >>> cross = Stream.crossWith (,)
--
-- The second stream is evaluated multiple times. If that is not desired it can
-- be cached in an 'Data.Array.Array' and then generated from the array before
-- calling this function. Caching may also improve performance if the stream is
-- expensive to evaluate.
--
-- See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused
-- alternative.
--
-- Time: O(m x n)
--
-- /Pre-release/
{-# INLINE cross #-}
cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)
cross = crossWith (,)
-------------------------------------------------------------------------------
-- Bind/Concat
-------------------------------------------------------------------------------
-- |
--
-- /CPS/
{-# INLINE bindWith #-}
bindWith
:: (Stream m b -> Stream m b -> Stream m b)
-> Stream m a
-> (a -> Stream m b)
-> Stream m b
bindWith par m1 f =
fromStreamK
$ K.bindWith
(\s1 s2 -> toStreamK $ par (fromStreamK s1) (fromStreamK s2))
(toStreamK m1)
(toStreamK . f)
-- | @concatMapWith mixer generator stream@ is a two dimensional looping
-- combinator. The @generator@ function is used to generate streams from the
-- elements in the input @stream@ and the @mixer@ function is used to merge
-- those streams.
--
-- /CPS/
{-# INLINE concatMapWith #-}
concatMapWith
:: (Stream m b -> Stream m b -> Stream m b)
-> (a -> Stream m b)
-> Stream m a
-> Stream m b
concatMapWith par f xs = bindWith par xs f

View File

@ -1,91 +0,0 @@
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Streamly.Internal.Data.Stream.Zip
-- Copyright : (c) 2017 Composewell Technologies
--
-- License : BSD3
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- To run examples in this module:
--
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Stream as Stream
-- >>> import qualified Streamly.Internal.Data.Stream.Zip as Stream
--
module Streamly.Internal.Data.Stream.Zip
(
ZipStream (..)
, ZipSerialM
, ZipSerial
)
where
import Data.Functor.Identity (Identity(..))
import GHC.Exts (IsList(..), IsString(..))
import Streamly.Internal.Data.Stream.Type (Stream)
import Text.Read
( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
, readListPrecDefault)
import qualified Streamly.Internal.Data.Stream.Bottom as Stream
import qualified Streamly.Internal.Data.Stream.Generate as Stream
-- $setup
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Stream as Stream
-- >>> import qualified Streamly.Internal.Data.Stream.Zip as Stream
------------------------------------------------------------------------------
-- Serially Zipping Streams
------------------------------------------------------------------------------
-- | For 'ZipStream':
--
-- @
-- (<>) = 'Streamly.Data.Stream.append'
-- (\<*>) = 'Streamly.Data.Stream.zipWith' id
-- @
--
-- Applicative evaluates the streams being zipped serially:
--
-- >>> s1 = Stream.ZipStream $ Stream.fromFoldable [1, 2]
-- >>> s2 = Stream.ZipStream $ Stream.fromFoldable [3, 4]
-- >>> s3 = Stream.ZipStream $ Stream.fromFoldable [5, 6]
-- >>> s = (,,) <$> s1 <*> s2 <*> s3
-- >>> Stream.fold Fold.toList (Stream.unZipStream s)
-- [(1,3,5),(2,4,6)]
--
newtype ZipStream m a = ZipStream {unZipStream :: Stream m a}
deriving (Functor, Semigroup, Monoid)
deriving instance IsList (ZipStream Identity a)
deriving instance (a ~ Char) => IsString (ZipStream Identity a)
deriving instance Eq a => Eq (ZipStream Identity a)
deriving instance Ord a => Ord (ZipStream Identity a)
deriving instance (Foldable m, Monad m) => Foldable (ZipStream m)
deriving instance Traversable (ZipStream Identity)
instance Show a => Show (ZipStream Identity a) where
showsPrec p dl = showParen (p > 10) $
showString "fromList " . shows (toList dl)
instance Read a => Read (ZipStream Identity a) where
readPrec = parens $ prec 10 $ do
Ident "fromList" <- lexP
fromList <$> readPrec
readListPrec = readListPrecDefault
type ZipSerialM = ZipStream
-- | An IO stream whose applicative instance zips streams serially.
--
type ZipSerial = ZipSerialM IO
instance Monad m => Applicative (ZipStream m) where
pure = ZipStream . Stream.repeat
{-# INLINE (<*>) #-}
ZipStream m1 <*> ZipStream m2 = ZipStream $ Stream.zipWith id m1 m2

View File

@ -21,7 +21,7 @@ module Streamly.Internal.Data.Unfold
-- * Unfolds
-- One to one correspondence with
-- "Streamly.Internal.Data.Stream.Generate"
-- "Streamly.Internal.Data.Stream.StreamD.Generate"
-- ** Basic Constructors
, mkUnfoldM
, mkUnfoldrM

View File

@ -422,23 +422,7 @@ library
if flag(dev)
exposed-modules:
Streamly.Internal.Data.Stream.StreamK.Alt
, Streamly.Internal.Data.Stream.Type
, Streamly.Internal.Data.Stream.Eliminate
, Streamly.Internal.Data.Stream.Enumerate
, Streamly.Internal.Data.Stream.Generate
, Streamly.Internal.Data.Stream.Transform
, Streamly.Internal.Data.Stream.Bottom
, Streamly.Internal.Data.Stream.Exception
, Streamly.Internal.Data.Stream.Expand
, Streamly.Internal.Data.Stream.Lift
, Streamly.Internal.Data.Stream.Reduce
, Streamly.Internal.Data.Stream.Transformer
, Streamly.Internal.Data.Stream.StreamDK
, Streamly.Internal.Data.Stream.Zip
, Streamly.Internal.Data.Stream.Cross
, Streamly.Internal.Data.List
, Streamly.Data.Stream.Zip
--, Streamly.Internal.Data.Parser.ParserDK
build-depends:
-- streamly-base