## Setup

To execute the code examples provided in this module in ghci, please run the following commands first.

`>>>`

`:m`

`>>>`

`import Streamly.Data.Unfold (Unfold)`

`>>>`

`import qualified Streamly.Data.Fold as Fold`

`>>>`

`import qualified Streamly.Data.Stream as Stream`

`>>>`

`import qualified Streamly.Data.Unfold as Unfold`

For APIs that have not been released yet.

`>>>`

`import qualified Streamly.Internal.Data.Unfold as Unfold`

## Unfold Type

## General Notes

What makes streams less efficient is also what makes them more convenient to use and powerful. The stream data type (Stream m a) bundles the state along with the stream generator function making it opaque, whereas an unfold exposes the state (Unfold m s a) to the user. This allows the Unfold to be unfolded (inlined) inside a nested loop without having to bundle the state and the generator together, the stream state can be saved and passed independent of the generator function. On the other hand in a stream type we have to bundle the stream state and the generator function together to save the stream. This makes it inefficient because it requires boxing and constructor allocation. However, this makes streams more convenient as we do not need to pass around the state/seed separately.

Unfold Type:

The order of arguments allows `Category`

and `Arrow`

instances but precludes
contravariant and contra-applicative.

## Unfolds and Streams

An `Unfold`

type is the same as the direct style `Stream`

type except that
it uses an inject function to determine the initial state of the stream
based on an input. A stream is a special case of Unfold when the static
input is unit or Void.

This allows an important optimization to occur in several cases, making the
`Unfold`

a more efficient abstraction. Consider the `concatMap`

and
`unfoldMany`

operations, the latter is more efficient. `concatMap`

generates a new stream object from each element in the stream by applying
the supplied function to the element, the stream object includes the "step"
function as well as the initial "state" of the stream. Since the stream is
generated dynamically the compiler does not know the step function or the
state type statically at compile time, therefore, it cannot inline it. On
the other hand in case of `unfoldMany`

the compiler has visibility into
the unfold's state generation function, therefore, the compiler knows all
the types statically and it can inline the inject as well as the step
functions, generating efficient code. Essentially, the stream is not opaque
to the consumer in case of unfolds, the consumer knows how to generate the
stream from a seed using a known "inject" and "step" functions.

A Stream is like a data object whereas unfold is like a function. Being
function like, an Unfold is an instance of `Category`

and `Arrow`

type
classes.

## Unfolds and Folds

Streams forcing a closed control flow loop can be categorized under two types, unfolds and folds, both of these are duals of each other.

Unfold streams are really generators of a sequence of elements, we can also call them pull style streams. These are lazy producers of streams. On each evaluation the producer generates the next element. A consumer can therefore pull elements from the stream whenever it wants to. A stream consumer can multiplex pull streams by pulling elements from the chosen streams, therefore, pull streams allow merging or multiplexing. On the other hand, with this representation we cannot split or demultiplex a stream. So really these are stream sources that can be generated from a seed and can be merged or zipped into a single stream.

The dual of Unfolds are Folds. Folds can also be called as push style streams or reducers. These are strict consumers of streams. We keep pushing elements to a fold and we can extract the result at any point. A driver can choose which fold to push to and can also push the same element to multiple folds. Therefore, folds allow splitting or demultiplexing a stream. On the other hand, we cannot merge streams using this representation. So really these are stream consumers that reduce the stream to a single value, these consumers can be composed such that a stream can be split over multiple consumers.

Performance:

Composing a tree or graph of computations with unfolds can be much more efficient compared to composing with the Monad instance. The reason is that unfolds allow the compiler to statically know the state and optimize it using stream fusion whereas it is not possible with the monad bind because the state is determined dynamically.

Reader:

An unfold acts as a reader (see `Reader`

monad). The input to an unfold acts
as the read-only environment. The environment can be extracted using the
`identity`

unfold (equivalent to `ask`

) and transformed using `lmap`

.

## Type

An `Unfold m a b`

is a generator of a stream of values of type `b`

from a
seed of type `a`

in `Monad`

`m`

.

## Basic Constructors

mkUnfoldM :: (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b Source #

Make an unfold from `step`

and `inject`

functions.

*Pre-release*

mkUnfoldrM :: Applicative m => (a -> m (Step a b)) -> Unfold m a b Source #

unfoldrM :: Applicative m => (a -> m (Maybe (b, a))) -> Unfold m a b Source #

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.

unfoldr :: Applicative m => (a -> Maybe (b, a)) -> Unfold m a b Source #

Like `unfoldrM`

but uses a pure step function.

`>>>`

f [] = Nothing f (x:xs) = Just (x, xs) :}`:{`

`>>>`

[1,2,3]`Unfold.fold Fold.toList (Unfold.unfoldr f) [1,2,3]`

functionM :: Applicative m => (a -> m b) -> Unfold m a b Source #

Lift a monadic function into an unfold. The unfold generates a singleton stream.

function :: Applicative m => (a -> b) -> Unfold m a b Source #

Lift a pure function into an unfold. The unfold generates a singleton stream.

function f = functionM $ return . f

identity :: Applicative m => Unfold m a a Source #

Identity unfold. The unfold generates a singleton stream having the input as the only element.

identity = function Prelude.id

*Pre-release*

## From Values

fromEffect :: Applicative m => m b -> Unfold m a b Source #

The unfold discards its input and generates a function stream using the supplied monadic action.

*Pre-release*

fromPure :: Applicative m => b -> Unfold m a b Source #

Discards the unfold input and always returns the argument of `fromPure`

.

fromPure = fromEffect . pure

*Pre-release*

## From Containers

fromList :: Applicative m => Unfold m [a] a Source #

Convert a list of pure values to a `Stream`

## Transformations

lmap :: (a -> c) -> Unfold m c b -> Unfold m a b Source #

Map a function on the input argument of the `Unfold`

.

`>>>`

`u = Unfold.lmap (fmap (+1)) Unfold.fromList`

`>>>`

[2,3,4,5,6]`Unfold.fold Fold.toList u [1..5]`

lmap f = Unfold.many (Unfold.function f)

lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b Source #

Map an action on the input argument of the `Unfold`

.

lmapM f = Unfold.many (Unfold.functionM f)

map :: Functor m => (b -> c) -> Unfold m a b -> Unfold m a c Source #

Map a function on the output of the unfold (the type `b`

).

`>>>`

`map f = Unfold.map2 (const f)`

*Pre-release*

map2 :: Functor m => (a -> b -> c) -> Unfold m a b -> Unfold m a c Source #

`>>>`

`map2 f = Unfold.mapM2 (\a b -> pure (f a b))`

Note that the seed may mutate (e.g. if the seed is a Handle or IORef) as stream is generated from it, so we need to be careful when reusing the seed while the stream is being generated from it.

mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c Source #

Apply a monadic function to each element of the stream and replace it with the output of the resulting action.

`>>>`

`mapM f = Unfold.mapM2 (const f)`

both :: a -> Unfold m a b -> Unfold m Void b Source #

Supply the seed to an unfold closing the input end of the unfold.

both a = Unfold.lmap (Prelude.const a)

*Pre-release*

first :: a -> Unfold m (a, b) c -> Unfold m b c Source #

Supply the first component of the tuple to an unfold that accepts a tuple as a seed resulting in a fold that accepts the second component of the tuple as a seed.

first a = Unfold.lmap (a, )

*Pre-release*

second :: b -> Unfold m (a, b) c -> Unfold m a c Source #

Supply the second component of the tuple to an unfold that accepts a tuple as a seed resulting in a fold that accepts the first component of the tuple as a seed.

second b = Unfold.lmap (, b)

*Pre-release*

## Trimming

takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b Source #

Same as `takeWhile`

but with a monadic predicate.

takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b Source #

End the stream generated by the `Unfold`

as soon as the predicate fails
on an element.

## Nesting

data ConcatState s1 s2 Source #

ConcatOuter s1 | |

ConcatInner s1 s2 |

many :: Monad m => Unfold m b c -> Unfold m a b -> Unfold m a c Source #

Apply the first unfold to each output element of the second unfold and flatten the output in a single stream.

`>>>`

`many u = Unfold.many2 (Unfold.lmap snd u)`

manyInterleave :: Monad m => Unfold m a b -> Unfold m c a -> Unfold m c b Source #

`unfoldManyInterleave`

for
documentation and notes.

This is almost identical to unfoldManyInterleave in StreamD module.

The `many`

combinator is in fact `manyAppend`

to be more explicit in naming.

*Internal*

crossApplySnd :: Unfold m a b -> Unfold m a c -> Unfold m a c Source #

Outer product discarding the first element.

*Unimplemented*

crossApplyFst :: Unfold m a b -> Unfold m a c -> Unfold m a b Source #

Outer product discarding the second element.

*Unimplemented*

crossWithM :: Monad m => (b -> c -> m d) -> Unfold m a b -> Unfold m a c -> Unfold m a d Source #

Create a cross product (vector product or cartesian product) of the output streams of two unfolds using a monadic combining function.

`>>>`

`f1 f u = Unfold.mapM2 (\(_, c) b -> f b c) (Unfold.lmap fst u)`

`>>>`

`crossWithM f u = Unfold.many2 (f1 f u)`

*Pre-release*

crossWith :: Monad m => (b -> c -> d) -> Unfold m a b -> Unfold m a c -> Unfold m a d Source #

Like `crossWithM`

but uses a pure combining function.

crossWith f = crossWithM (\b c -> return $ f b c)

`>>>`

`u1 = Unfold.lmap fst Unfold.fromList`

`>>>`

`u2 = Unfold.lmap snd Unfold.fromList`

`>>>`

`u = Unfold.crossWith (,) u1 u2`

`>>>`

[(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]`Unfold.fold Fold.toList u ([1,2,3], [4,5,6])`

cross :: Monad m => Unfold m a b -> Unfold m a c -> Unfold m a (b, c) Source #

See `crossWith`

.

Definition:

`>>>`

`cross = Unfold.crossWith (,)`

To create a cross product of the streams generated from a tuple we can write:

`>>>`

cross :: Monad m => Unfold m a b -> Unfold m c d -> Unfold m (a, c) (b, d) cross u1 u2 = Unfold.cross (Unfold.lmap fst u1) (Unfold.lmap snd u2) :}`:{`

*Pre-release*

concatMapM :: Monad m => (b -> m (Unfold m a c)) -> Unfold m a b -> Unfold m a c Source #

Map an unfold generating action to each element of an unfold and flatten the results into a single stream.

zipWithM :: Monad m => (b -> c -> m d) -> Unfold m a b -> Unfold m a c -> Unfold m a d Source #

Distribute the input to two unfolds and then zip the outputs to a single stream using a monadic zip function.

Stops as soon as any of the unfolds stops.

*Pre-release*

zipWith :: Monad m => (b -> c -> d) -> Unfold m a b -> Unfold m a c -> Unfold m a d Source #

Like `zipWithM`

but with a pure zip function.

`>>>`

`square = fmap (\x -> x * x) Unfold.fromList`

`>>>`

`cube = fmap (\x -> x * x * x) Unfold.fromList`

`>>>`

`u = Unfold.zipWith (,) square cube`

`>>>`

[(1,1),(4,8),(9,27),(16,64),(25,125)]`Unfold.fold Fold.toList u [1..5]`

zipWith f = zipWithM (\a b -> return $ f a b)

## Unfolds

### Basic Constructors

nilM :: Applicative m => (a -> m c) -> Unfold m a b Source #

Lift a monadic function into an unfold generating a nil stream with a side effect.

nil :: Applicative m => Unfold m a b Source #

An empty stream.

consM :: Applicative m => (a -> m b) -> Unfold m a b -> Unfold m a b Source #

Prepend a monadic single element generator function to an `Unfold`

. The
same seed is used in the action as well as the unfold.

*Pre-release*

### Generators

Generate a monadic stream from a seed.

repeatM :: Applicative m => Unfold m (m a) a Source #

Generates an infinite stream repeating the seed.

replicateM :: Applicative m => Unfold m (Int, m a) a Source #

Given a seed `(n, action)`

, generates a stream replicating the `action`

`n`

times.

fromIndicesM :: Applicative m => (Int -> m a) -> Unfold m Int a Source #

`fromIndicesM gen`

generates an infinite stream of values using `gen`

starting from the seed.

fromIndicesM f = Unfold.mapM f $ Unfold.enumerateFrom 0

*Pre-release*

iterateM :: Applicative m => (a -> m a) -> Unfold m (m a) a Source #

Generates an infinite stream starting with the given seed and applying the given function repeatedly.

### Enumerations

class Enum a => Enumerable a where Source #

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.Unfold.Enumeration module to define new instances.

*Pre-release*

enumerateFrom :: Monad m => Unfold m a a Source #

Unfolds `from`

generating 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`

.

`>>>`

[0,1,2,3]`Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom (0 :: Int)`

For `Fractional`

types, enumeration is numerically stable. However, no
overflow or underflow checks are performed.

`>>>`

[1.1,2.1,3.1,4.1]`Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom 1.1`

*Pre-release*

enumerateFromTo :: Monad m => Unfold m (a, a) a Source #

Unfolds `(from, to)`

generating 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.

`>>>`

[0,1,2,3,4]`Stream.toList $ Stream.unfold Unfold.enumerateFromTo (0, 4)`

For `Fractional`

types, the last element is equal to the specified `to`

value after rounding to the nearest integral value.

`>>>`

[1.1,2.1,3.1,4.1]`Stream.toList $ Stream.unfold Unfold.enumerateFromTo (1.1, 4)`

`>>>`

[1.1,2.1,3.1,4.1,5.1]`Stream.toList $ Stream.unfold Unfold.enumerateFromTo (1.1, 4.6)`

*Pre-release*

enumerateFromThen :: Monad m => Unfold m (a, a) a Source #

Unfolds `(from, then)`

generating a stream whose first element is
`from`

and the successive elements are in increments of `then`

. 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.

`>>>`

[0,2,4,6]`Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFromThen (0, 2)`

`>>>`

[0,-2,-4,-6]`Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFromThen (0,(-2))`

*Pre-release*

enumerateFromThenTo :: Monad m => Unfold m (a, a, a) a Source #

Unfolds `(from, then, to)`

generating a finite stream whose first element
is `from`

and the successive elements are in increments of `then`

up to
`to`

. Enumeration can occur downwards or upwards depending on whether `then`

comes before or after `from`

.

`>>>`

[0,2,4,6]`Stream.toList $ Stream.unfold Unfold.enumerateFromThenTo (0, 2, 6)`

`>>>`

[0,-2,-4,-6]`Stream.toList $ Stream.unfold Unfold.enumerateFromThenTo (0, (-2), (-6))`

*Pre-release*

##### Instances

### Enumerating `Num`

Types

enumerateFromStepNum :: (Monad m, Num a) => Unfold m (a, a) a Source #

Unfolds `(from, stride)`

generating an infinite stream starting from
`from`

and incrementing every time by `stride`

. For `Bounded`

types, after
the value overflows it keeps enumerating in a cycle:

>>> Stream.toList $ Stream.take 10 $ Stream.unfold Unfold.enumerateFromStepNum (255::Word8,1) [255,0,1,2,3,4,5,6,7,8]

The implementation is numerically stable for floating point values.

Note `enumerateFromStepIntegral`

is faster for integrals.

*Internal*

enumerateFromNum :: (Monad m, Num a) => Unfold m a a Source #

Same as `enumerateFromStepNum`

using a stride of 1:

>>> enumerateFromNum = lmap (from -> (from, 1)) Unfold.enumerateFromStepNum >>> Stream.toList $ Stream.take 6 $ Stream.unfold enumerateFromNum (0.9) [0.9,1.9,2.9,3.9,4.9,5.9]

Also, same as `enumerateFromThenNum`

using a stride of 1 but see the note in
`enumerateFromThenNum`

about the loss of precision:

>>> enumerateFromNum = lmap (from -> (from, from + 1)) Unfold.enumerateFromThenNum >>> Stream.toList $ Stream.take 6 $ Stream.unfold enumerateFromNum (0.9) [0.9,1.9,2.9,3.8999999999999995,4.8999999999999995,5.8999999999999995]

*Internal*

enumerateFromThenNum :: (Monad m, Num a) => Unfold m (a, a) a Source #

Same as 'enumerateFromStepNum (from, next)' using a stride of `next - from`

:

>>> enumerateFromThenNum = lmap ((from, next) -> (from, next - from)) Unfold.enumerateFromStepNum

Example: @ >>> Stream.toList $ Stream.take 10 $ Stream.unfold enumerateFromThenNum (255::Word8,0) [255,0,1,2,3,4,5,6,7,8]

```
The implementation is numerically stable for floating point values.
Note that
````enumerateFromThenIntegral`

is faster for integrals.
Note that in the strange world of floating point numbers, using

enumerateFromThenNum (from, from + 1)```
is almost exactly the same as
```

enumerateFromStepNum (from, 1) but not precisely the same. Because ```
(from +
1) - from
```

is not exactly 1, it may lose some precision, the loss may also
be aggregated in each step, if you want that precision then use
`enumerateFromStepNum`

instead.

*Internal*

### Enumerating unbounded `Integral`

Types

enumerateFromStepIntegral :: (Monad m, Integral a) => Unfold m (a, a) a Source #

Can be used to enumerate unbounded integrals. This does not check for overflow or underflow for bounded integrals.

*Internal*

### Enumerating `Bounded`

`Integral`

Types

enumerateFromThenToIntegralBounded :: (Monad m, Integral a, Bounded a) => Unfold m (a, a, a) a Source #

### Enumerating small `Integral`

Types

Small types are always bounded.

enumerateFromSmallBounded :: (Monad m, Enum a, Bounded a) => Unfold m a a Source #

Enumerate from given starting Enum value `from`

with stride of 1 till
maxBound

*Internal*

enumerateFromThenSmallBounded :: forall m a. (Monad m, Enum a, Bounded a) => Unfold m (a, a) a Source #

Enumerate from given starting Enum value `from`

and next Enum value `next`

with stride of (fromEnum next - fromEnum from) till maxBound.

*Internal*

enumerateFromToSmall :: (Monad m, Enum a) => Unfold m (a, a) a Source #

Enumerate from given starting Enum value `from`

and to Enum value `to`

with stride of 1 till to value.

*Internal*

enumerateFromThenToSmall :: (Monad m, Enum a) => Unfold m (a, a, a) a Source #

Enumerate from given starting Enum value `from`

and then Enum value `next`

and to Enum value `to`

with stride of (fromEnum next - fromEnum from)
till to value.

*Internal*

### Enumerating `Fractional`

Types

Enumeration of `Num`

specialized to `Fractional`

types.

enumerateFromFractional :: (Monad m, Fractional a) => Unfold m a a Source #

enumerateFromThenFractional :: (Monad m, Fractional a) => Unfold m (a, a) a Source #

enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => Unfold m (a, a) a Source #

Same as `enumerateFromStepNum`

with a step of 1 and enumerating up to the
specified upper limit rounded to the nearest integral value:

>>> Stream.toList $ Stream.unfold Unfold.enumerateFromToFractional (0.1, 6.3) [0.1,1.1,2.1,3.1,4.1,5.1,6.1]

*Internal*

enumerateFromThenToFractional :: (Monad m, Fractional a, Ord a) => Unfold m (a, a, a) a Source #

### From Containers

### From Memory

### From Stream

fromStreamK :: Applicative m => Unfold m (StreamK m a) a Source #

fromStreamD :: Applicative m => Unfold m (Stream m a) a Source #

fromStream :: Applicative m => Unfold m (Stream m a) a Source #

## Combinators

### Mapping on Input

discardFirst :: Unfold m a b -> Unfold m (c, a) b Source #

Convert an `Unfold`

into an unfold accepting a tuple as an argument,
using the argument of the original fold as the second element of tuple and
discarding the first element of the tuple.

discardFirst = Unfold.lmap snd

*Pre-release*

discardSecond :: Unfold m a b -> Unfold m (a, c) b Source #

Convert an `Unfold`

into an unfold accepting a tuple as an argument,
using the argument of the original fold as the first element of tuple and
discarding the second element of the tuple.

discardSecond = Unfold.lmap fst

*Pre-release*

swap :: Unfold m (a, c) b -> Unfold m (c, a) b Source #

Convert an `Unfold`

that accepts a tuple as an argument into an unfold
that accepts a tuple with elements swapped.

swap = Unfold.lmap Tuple.swap

*Pre-release*

## Folding

### Mapping on Output

postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b Source #

Scan the output of an `Unfold`

to change it in a stateful manner.

*Pre-release*

postscan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c Source #

Scan the output of an `Unfold`

to change it in a stateful manner.

*Pre-release*

scan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c Source #

Scan the output of an `Unfold`

to change it in a stateful manner.
Once fold is done it will stop.

`>>>`

`u = Unfold.scan (Fold.take 2 Fold.sum) Unfold.fromList`

`>>>`

[0,1,3]`Unfold.fold Fold.toList u [1,2,3,4,5]`

*Pre-release*

scanMany :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c Source #

Scan the output of an `Unfold`

to change it in a stateful manner.
Once fold is done it will restart from its initial state.

`>>>`

`u = Unfold.scanMany (Fold.take 2 Fold.sum) Unfold.fromList`

`>>>`

[0,1,3,0,3,7,0,5]`Unfold.fold Fold.toList u [1,2,3,4,5]`

*Pre-release*

foldMany :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c Source #

Apply a fold multiple times on the output of an unfold.

*Pre-release*

### Either Wrapped Input

either :: Applicative m => Unfold m a c -> Unfold m b c -> Unfold m (Either a b) c Source #

Choose left or right unfold based on an either input.

*Pre-release*

### Filtering

take :: Applicative m => Int -> Unfold m a b -> Unfold m a b Source #

`>>>`

`u = Unfold.take 2 Unfold.fromList`

`>>>`

[1,2]`Unfold.fold Fold.toList u [1..100]`

filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b Source #

Include only those elements that pass a predicate.

filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b Source #

Same as `filter`

but with a monadic predicate.

drop :: Applicative m => Int -> Unfold m a b -> Unfold m a b Source #

`drop n unf`

drops `n`

elements from the stream generated by `unf`

.

dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b Source #

Similar to `dropWhileM`

but with a pure condition function.

dropWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b Source #

`dropWhileM f unf`

drops elements from the stream generated by `unf`

while
the condition holds true. The condition function `f`

is *monadic* in nature.

### Cross product

joinInnerGeneric :: Monad m => (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c) Source #

### 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.

:: Monad m | |

=> (a -> m c) | before |

-> (forall s. m s -> m (Either e s)) | try (exception handling) |

-> (c -> m d) | after, on normal stop |

-> Unfold m (c, e) b | on exception |

-> Unfold m c b | unfold to run |

-> Unfold m a b |

Like `gbracketIO`

but with following differences:

- alloc action
`a -> m c`

runs with async exceptions enabled - cleanup action
`c -> m d`

won't run if the stream is garbage collected after partial evaluation.

*Inhibits stream fusion*

*Pre-release*

:: MonadIO m | |

=> (a -> IO c) | before |

-> (c -> IO d) | after, on normal stop, or GC |

-> (c -> IO ()) | action on exception |

-> Unfold m e b | stream on exception |

-> (forall s. m s -> IO (Either e s)) | try (exception handling) |

-> Unfold m c b | unfold to run |

-> Unfold m a b |

Run the alloc action `a -> m c`

with async exceptions disabled but keeping
blocking operations interruptible (see `mask`

). Use the
output `c`

as input to `Unfold m c b`

to generate an output stream. When
unfolding use the supplied `try`

operation `forall s. m s -> m (Either e s)`

to catch synchronous exceptions. If an exception occurs run the exception
handling unfold `Unfold m (c, e) b`

.

The cleanup action `c -> m d`

, runs whenever the stream ends normally, due
to a sync or async exception or if it gets garbage collected after a partial
lazy evaluation. See `bracket`

for the semantics of the cleanup action.

`gbracket`

can express all other exception handling combinators.

*Inhibits stream fusion*

*Pre-release*

before :: (a -> m c) -> Unfold m a b -> Unfold m a b Source #

Run a side effect `a -> m c`

on the input `a`

before unfolding it using
`Unfold m a b`

.

before f = lmapM (\a -> f a >> return a)

*Pre-release*

afterIO :: MonadIO m => (a -> IO c) -> Unfold m a b -> Unfold m a b Source #

Unfold the input `a`

using `Unfold m a b`

, run an action on `a`

whenever
the unfold stops normally, or if it is garbage collected after a partial
lazy evaluation.

The semantics of the action `a -> m c`

are similar to the cleanup action
semantics in `bracket`

.

*See also after_*

*Pre-release*

after_ :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b Source #

Like `after`

with following differences:

- action
`a -> m c`

won't run if the stream is garbage collected after partial evaluation. - Monad
`m`

does not require any other constraints.

*Pre-release*

finallyIO :: (MonadIO m, MonadCatch m) => (a -> IO c) -> Unfold m a b -> Unfold m a b Source #

Unfold the input `a`

using `Unfold m a b`

, run an action on `a`

whenever
the unfold stops normally, aborts due to an exception or if it is garbage
collected after a partial lazy evaluation.

The semantics of the action `a -> m c`

are similar to the cleanup action
semantics in `bracket`

.

finally release = bracket return release

*See also finally_*

*Inhibits stream fusion*

*Pre-release*

finally_ :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b Source #

Like `finallyIO`

with following differences:

- action
`a -> m c`

won't run if the stream is garbage collected after partial evaluation.

*Inhibits stream fusion*

*Pre-release*

bracketIO :: (MonadIO m, MonadCatch m) => (a -> IO c) -> (c -> IO d) -> Unfold m c b -> Unfold m a b Source #

Run the alloc action `a -> m c`

with async exceptions disabled but keeping
blocking operations interruptible (see `mask`

). Use the
output `c`

as input to `Unfold m c b`

to generate an output stream.

`c`

is usually a resource under the state of monad `m`

, e.g. a file
handle, that requires a cleanup after use. The cleanup action `c -> m d`

,
runs whenever the stream ends normally, due to a sync or async exception or
if it gets garbage collected after a partial lazy evaluation.

`bracket`

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: bracket_, gbracket*

*Inhibits stream fusion*

*Pre-release*

bracket_ :: MonadCatch m => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b Source #

Like `bracketIO`

but with following differences:

- alloc action
`a -> m c`

runs with async exceptions enabled - cleanup action
`c -> m d`

won't run if the stream is garbage collected after partial evaluation.

*Inhibits stream fusion*

*Pre-release*

### 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.

onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b Source #

Unfold the input `a`

using `Unfold m a b`

, run the action `a -> m c`

on
`a`

if the unfold aborts due to an exception.

*Inhibits stream fusion*

*Pre-release*