mirror of
https://github.com/composewell/streamly.git
synced 2024-09-19 07:29:02 +03:00
Remove obselete files and fix documentation and dev module imports
This commit is contained in:
parent
5f4ba50e09
commit
128ac005df
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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
|
@ -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
|
||||
|
@ -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'
|
||||
--
|
||||
|
@ -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)
|
@ -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
|
@ -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)
|
@ -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)
|
||||
-}
|
@ -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
|
@ -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))
|
@ -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
|
@ -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)
|
@ -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
|
@ -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
|
||||
--
|
||||
|
@ -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
@ -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)
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user