linear-base-0.4.0: src/Streaming/Linear.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE QualifiedDo #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-name-shadowing #-}
module Streaming.Linear
( -- $stream
module Streaming.Linear.Internal.Type,
-- * Constructing a 'Stream' on a given functor
yields,
effect,
wrap,
replicates,
replicatesM,
unfold,
untilJust,
streamBuild,
delays,
-- * Transforming streams
maps,
mapsPost,
mapsM,
mapsMPost,
mapped,
mappedPost,
hoistUnexposed,
groups,
-- * Inspecting a stream
inspect,
-- * Splitting and joining 'Stream's
splitsAt,
chunksOf,
concats,
intercalates,
-- * Zipping, unzipping, separating and unseparating streams
unzips,
separate,
unseparate,
decompose,
expand,
expandPost,
-- * Eliminating a 'Stream'
mapsM_,
run,
streamFold,
iterTM,
iterT,
destroy,
)
where
import Control.Concurrent (threadDelay)
import qualified Control.Functor.Linear as Control
import Data.Functor.Compose
import qualified Data.Functor.Linear as Data
import Data.Functor.Sum
import Data.Unrestricted.Linear
import GHC.Stack
import Prelude.Linear (($), (&), (.))
import Streaming.Linear.Internal.Process (destroyExposed)
import Streaming.Linear.Internal.Type
import qualified Streaming.Prelude.Linear as Stream
import System.IO.Linear
import Prelude
( Double,
Either (..),
Int,
Maybe (..),
Num (..),
Ord (..),
Ordering (..),
fromInteger,
)
import qualified Prelude
-- $stream
-- The 'Stream' data type is an effectful series of steps with some
-- payload value at the bottom. The steps are represented with functors.
-- The effects are represented with some /control/ monad. (Control monads
-- must be bound to exactly once; see the documentation in
-- <https://github.com/tweag/linear-base/tree/master/src/Control/Monad/Linear.hs linear-base> to learn more
-- about control monads, control applicatives and control functors.)
--
-- In words, a @Stream f m r@ is either a payload of type @r@, or
-- a step of type @f (Stream f m r)@ or an effect of type @m (Stream f m r)@
-- where @f@ is a @Control.Functor@ and @m@ is a @Control.Monad@.
--
-- This module exports combinators that pertain to this general case.
-- Some of these are quite abstract and pervade any use of the library,
-- e.g.
--
-- > maps :: (forall x . f x %1-> g x) -> Stream f m r %1-> Stream g m r
-- > mapped :: (forall x. f x %1-> m (g x)) -> Stream f m r %1-> Stream g m r
-- > concats :: Stream (Stream f m) m r %1-> Stream f m r
--
-- (assuming here and thoughout that @m@ or @n@ satisfies
-- a @Control.Monad@ constraint, and @f@ or @g@ a @Control.Functor@
-- constraint).
--
-- Others are surprisingly determinate in content:
--
-- > chunksOf :: Int -> Stream f m r %1-> Stream (Stream f m) m r
-- > splitsAt :: Int -> Stream f m r %1-> Stream f m (Stream f m r)
-- > intercalates :: Stream f m () -> Stream (Stream f m) m r %1-> Stream f m r
-- > unzips :: Stream (Compose f g) m r %1-> Stream f (Stream g m) r
-- > separate :: Stream (Sum f g) m r -> Stream f (Stream g m) r -- cp. partitionEithers
-- > unseparate :: Stream f (Stream g) m r -> Stream (Sum f g) m r
-- > groups :: Stream (Sum f g) m r %1-> Stream (Sum (Stream f m) (Stream g m)) m r
--
-- One way to see that /any/ streaming library needs some such general type is
-- that it is required to represent the segmentation of a stream, and to
-- express the equivalents of @Prelude/Data.List@ combinators that involve
-- 'lists of lists' and the like. See for example this
-- <http://www.haskellforall.com/2013/09/perfect-streaming-using-pipes-bytestring.html post>
-- on the correct expression of a streaming \'lines\' function.
-- The module @Streaming.Prelude@ exports combinators relating to
-- > Stream (Of a) m r
-- where @Of a r = !a :> r@ is a left-strict pair.
-- This expresses the concept of a 'Producer' or 'Source' or 'Generator' and
-- easily inter-operates with types with such names in e.g. 'conduit',
-- 'iostreams' and 'pipes'.
-- # Constructing a 'Stream' on a given functor
-------------------------------------------------------------------------------
-- Remark. By default we require `Control.Monad` and `Control.Functor`
-- instances for the `m` and `f` in a `Stream f m r` since these allow the
-- stream to have a `Control.Monad` instance
-- | @yields@ is like @lift@ for items in the streamed functor.
-- It makes a singleton or one-layer succession.
--
-- > lift :: (Control.Monad m, Control.Functor f) => m r %1-> Stream f m r
-- > yields :: (Control.Monad m, Control.Functor f) => f r %1-> Stream f m r
--
-- Viewed in another light, it is like a functor-general version of @yield@:
--
-- > S.yield a = yields (a :> ())
yields :: (Control.Monad m, Control.Functor f) => f r %1 -> Stream f m r
yields fr = Step $ Control.fmap Return fr
{-# INLINE yields #-}
-- Note: This must consume its input linearly since it must bind to a
-- `Control.Monad`.
-- | Wrap an effect that returns a stream
--
-- > effect = join . lift
effect ::
(Control.Monad m, Control.Functor f) =>
m (Stream f m r) %1 ->
Stream f m r
effect = Effect
{-# INLINE effect #-}
-- | Wrap a new layer of a stream. So, e.g.
--
-- > S.cons :: Control.Monad m => a -> Stream (Of a) m r %1-> Stream (Of a) m r
-- > S.cons a str = wrap (a :> str)
--
-- and, recursively:
--
-- > S.each' :: Control.Monad m => [a] -> Stream (Of a) m ()
-- > S.each' = foldr (\a b -> wrap (a :> b)) (return ())
--
-- The two operations
--
-- > wrap :: (Control.Monad m, Control.Functor f) =>
-- > f (Stream f m r) %1-> Stream f m r
-- > effect :: (Control.Monad m, Control.Functor f) =>
-- > m (Stream f m r) %1-> Stream f m r
--
-- are fundamental. We can define the parallel operations @yields@ and @lift@
-- in terms of them
--
-- > yields :: (Control.Monad m, Control.Functor f) => f r %1-> Stream f m r
-- > yields = wrap . Control.fmap Control.return
-- > lift :: (Control.Monad m, Control.Functor f) => m r %1-> Stream f m r
-- > lift = effect . Control.fmap Control.return
wrap ::
(Control.Monad m, Control.Functor f) =>
f (Stream f m r) %1 ->
Stream f m r
wrap = Step
{-# INLINE wrap #-}
-- | Repeat a functorial layer, command or instruction a fixed number of times.
replicates ::
(HasCallStack, Control.Monad m, Control.Functor f) =>
Int ->
f () ->
Stream f m ()
replicates n f = replicates' n f
where
replicates' ::
(HasCallStack, Control.Monad m, Control.Functor f) =>
Int ->
f () ->
Stream f m ()
replicates' n f = case compare n 0 of
LT -> Prelude.error "replicates called with negative integer"
EQ -> Return ()
GT -> Step $ Control.fmap (\() -> replicates (n - 1) f) f
{-# INLINE replicates #-}
-- | @replicatesM n@ repeats an effect containing a functorial layer, command
-- or instruction @n@ times.
replicatesM ::
forall f m.
(Control.Monad m, Control.Functor f) =>
Int ->
m (f ()) ->
Stream f m ()
replicatesM = loop
where
loop :: Int -> m (f ()) -> Stream f m ()
loop n mfstep
| n <= 0 = Return ()
| Prelude.otherwise =
Effect $
Control.fmap (Step . Control.fmap (\() -> loop (n - 1) mfstep)) mfstep
{-# INLINEABLE replicatesM #-}
unfold ::
(Control.Monad m, Control.Functor f) =>
(s %1 -> m (Either r (f s))) ->
s %1 ->
Stream f m r
unfold step state = unfold' step state
where
unfold' ::
(Control.Monad m, Control.Functor f) =>
(s %1 -> m (Either r (f s))) ->
s %1 ->
Stream f m r
unfold' step state = Effect $ Control.do
either <- step state
either & \case
Left r -> Control.return $ Return r
Right (fs) -> Control.return $ Step $ Control.fmap (unfold step) fs
{-# INLINEABLE unfold #-}
-- Note. To keep restrictions minimal, we use the `Data.Applicative`
-- instance.
untilJust ::
forall f m r.
(Control.Monad m, Data.Applicative f) =>
m (Maybe r) ->
Stream f m r
untilJust action = loop
where
loop :: Stream f m r
loop = Effect $ Control.do
maybeVal <- action
maybeVal & \case
Nothing -> Control.return $ Step $ Data.pure loop
Just r -> Control.return $ Return r
{-# INLINEABLE untilJust #-}
-- Remark. The linear church encoding of streams has linear
-- return, effect and step functions.
-- | Reflect a church-encoded stream; cp. @GHC.Exts.build@
--
-- > streamFold return_ effect_ step_ (streamBuild psi) = psi return_ effect_ step_
streamBuild ::
(forall b. (r %1 -> b) -> (m b %1 -> b) -> (f b %1 -> b) -> b) -> Stream f m r
streamBuild = \phi -> phi Return Effect Step
{-# INLINE streamBuild #-}
-- Note. To keep requirements minimal, we use the `Data.Applicative`
-- instance instead of the `Control.Applicative` instance.
delays :: forall f r. (Data.Applicative f) => Double -> Stream f IO r
delays seconds = loop
where
loop :: Stream f IO r
loop = Effect $ Control.do
let delay = fromInteger (Prelude.truncate (1000000 * seconds))
() <- fromSystemIO $ threadDelay delay
Control.return $ Step $ Data.pure loop
{-# INLINEABLE delays #-}
-- # Transforming streams
-------------------------------------------------------------------------------
-- | Map layers of one functor to another with a transformation.
--
-- > maps id = id
-- > maps f . maps g = maps (f . g)
maps ::
forall f g m r.
(Control.Monad m, Control.Functor f) =>
(forall x. f x %1 -> g x) ->
Stream f m r %1 ->
Stream g m r
maps = Stream.maps
{-# INLINE maps #-}
-- | Map layers of one functor to another with a transformation.
--
-- > mapsPost id = id
-- > mapsPost f . mapsPost g = mapsPost (f . g)
-- > mapsPost f = maps f
--
-- @mapsPost@ is essentially the same as 'maps', but it imposes a @Control.Functor@ constraint on
-- its target functor rather than its source functor. It should be preferred if @Control.fmap@
-- is cheaper for the target functor than for the source functor.
mapsPost ::
forall m f g r.
(Control.Monad m, Control.Functor g) =>
(forall x. f x %1 -> g x) ->
Stream f m r %1 ->
Stream g m r
mapsPost = Stream.mapsPost
{-# INLINE mapsPost #-}
-- Note. The transformation function must be linear so that the stream
-- held inside a control functor is used linearly.
-- | Map layers of one functor to another with a transformation involving the base monad.
-- 'maps' is more fundamental than @mapsM@, which is best understood as a convenience
-- for effecting this frequent composition:
--
-- > mapsM phi = decompose . maps (Compose . phi)
--
-- The streaming prelude exports the same function under the better name @mapped@,
-- which overlaps with the lens libraries.
mapsM ::
forall f g m r.
(Control.Monad m, Control.Functor f) =>
(forall x. f x %1 -> m (g x)) ->
Stream f m r %1 ->
Stream g m r
mapsM transform = loop
where
loop :: Stream f m r %1 -> Stream g m r
loop stream =
stream & \case
Return r -> Return r
Step f -> Effect $ Control.fmap Step $ transform $ Control.fmap loop f
Effect m -> Effect $ Control.fmap loop m
{-# INLINE mapsM #-}
-- | Map layers of one functor to another with a transformation involving the base monad.
-- @mapsMPost@ is essentially the same as 'mapsM', but it imposes a @Control.Functor@ constraint on
-- its target functor rather than its source functor. It should be preferred if @Control.fmap@
-- is cheaper for the target functor than for the source functor.
--
-- @mapsPost@ is more fundamental than @mapsMPost@, which is best understood as a convenience
-- for effecting this frequent composition:
--
-- > mapsMPost phi = decompose . mapsPost (Compose . phi)
--
-- The streaming prelude exports the same function under the better name @mappedPost@,
-- which overlaps with the lens libraries.
mapsMPost ::
forall m f g r.
(Control.Monad m, Control.Functor g) =>
(forall x. f x %1 -> m (g x)) ->
Stream f m r %1 ->
Stream g m r
mapsMPost = Stream.mapsMPost
{-# INLINE mapsMPost #-}
-- | Map layers of one functor to another with a transformation involving the base monad.
-- This could be trivial, e.g.
--
-- > let noteBeginning text x = (fromSystemIO (System.putStrLn text)) Control.>> (Control.return x)
--
-- this is completely functor-general
--
-- @maps@ and @mapped@ obey these rules:
--
-- > maps id = id
-- > mapped return = id
-- > maps f . maps g = maps (f . g)
-- > mapped f . mapped g = mapped (f <=< g)
-- > maps f . mapped g = mapped (fmap f . g)
-- > mapped f . maps g = mapped (f <=< fmap g)
--
-- @maps@ is more fundamental than @mapped@, which is best understood as a convenience
-- for effecting this frequent composition:
--
-- > mapped phi = decompose . maps (Compose . phi)
mapped ::
forall f g m r.
(Control.Monad m, Control.Functor f) =>
(forall x. f x %1 -> m (g x)) ->
Stream f m r %1 ->
Stream g m r
mapped = mapsM
{-# INLINE mapped #-}
-- | A version of 'mapped' that imposes a @Control.Functor@ constraint on the target functor rather
-- than the source functor. This version should be preferred if @Control.fmap@ on the target
-- functor is cheaper.
mappedPost ::
forall m f g r.
(Control.Monad m, Control.Functor g) =>
(forall x. f x %1 -> m (g x)) ->
Stream f m r %1 ->
Stream g m r
mappedPost = mapsMPost
{-# INLINE mappedPost #-}
-- | A less-efficient version of 'hoist' that works properly even when its
-- argument is not a monad morphism.
hoistUnexposed ::
forall f m n r.
(Control.Monad m, Control.Functor f) =>
(forall a. m a %1 -> n a) ->
Stream f m r %1 ->
Stream f n r
hoistUnexposed trans = loop
where
loop :: Stream f m r %1 -> Stream f n r
loop =
Effect
. trans
. inspectC
(Control.return . Return)
(Control.return . Step . Control.fmap loop)
{-# INLINEABLE hoistUnexposed #-}
-- A version of 'inspect' that takes explicit continuations.
-- Note that due to the linear constructors of 'Stream', these continuations
-- are linear.
inspectC ::
forall f m r a.
(Control.Monad m) =>
(r %1 -> m a) ->
(f (Stream f m r) %1 -> m a) ->
Stream f m r %1 ->
m a
inspectC f g = loop
where
loop :: Stream f m r %1 -> m a
loop (Return r) = f r
loop (Step x) = g x
loop (Effect m) = m Control.>>= loop
{-# INLINE inspectC #-}
-- | Group layers in an alternating stream into adjoining sub-streams
-- of one type or another.
groups ::
forall f g m r.
(Control.Monad m, Control.Functor f, Control.Functor g) =>
Stream (Sum f g) m r %1 ->
Stream (Sum (Stream f m) (Stream g m)) m r
groups = loop
where
loop :: Stream (Sum f g) m r %1 -> Stream (Sum (Stream f m) (Stream g m)) m r
loop str = Control.do
e <- Control.lift $ inspect str
e & \case
Left r -> Control.return r
Right ostr ->
ostr & \case
InR gstr -> Step $ InR $ Control.fmap loop $ cleanR (Step (InR gstr))
InL fstr -> Step $ InL $ Control.fmap loop $ cleanL (Step (InL fstr))
cleanL :: Stream (Sum f g) m r %1 -> Stream f m (Stream (Sum f g) m r)
cleanL = go
where
go :: Stream (Sum f g) m r %1 -> Stream f m (Stream (Sum f g) m r)
go s = Control.do
e <- Control.lift $ inspect s
e & \case
Left r -> Control.return $ Control.return r
Right (InL fstr) -> Step $ Control.fmap go fstr
Right (InR gstr) -> Control.return $ Step (InR gstr)
cleanR :: Stream (Sum f g) m r %1 -> Stream g m (Stream (Sum f g) m r)
cleanR = go
where
go :: Stream (Sum f g) m r %1 -> Stream g m (Stream (Sum f g) m r)
go s = Control.do
e <- Control.lift $ inspect s
e & \case
Left r -> Control.return $ Control.return r
Right (InL fstr) -> Control.return $ Step (InL fstr)
Right (InR gstr) -> Step $ Control.fmap go gstr
{-# INLINEABLE groups #-}
-- # Inspecting a Stream
-------------------------------------------------------------------------------
-- | Inspect the first stage of a freely layered sequence.
-- Compare @Pipes.next@ and the replica @Streaming.Prelude.next@.
-- This is the 'uncons' for the general 'unfold'.
--
-- > unfold inspect = id
-- > Streaming.Prelude.unfoldr StreamingPrelude.next = id
inspect ::
forall f m r.
(Control.Monad m) =>
Stream f m r %1 ->
m (Either r (f (Stream f m r)))
inspect = loop
where
loop :: Stream f m r %1 -> m (Either r (f (Stream f m r)))
loop stream =
stream & \case
Return r -> Control.return (Left r)
Effect m -> m Control.>>= loop
Step fs -> Control.return (Right fs)
{-# INLINEABLE inspect #-}
-- # Splitting and joining 'Stream's
-------------------------------------------------------------------------------
-- | Split a succession of layers after some number, returning a streaming or
-- effectful pair.
--
-- \>\>\> rest <- S.print $ S.splitAt 1 $ each' [1..3]
-- 1
-- \>\>\> S.print rest
-- 2
-- 3
--
-- > splitAt 0 = return
-- > (\stream -> splitAt n stream >>= splitAt m) = splitAt (m+n)
--
-- Thus, e.g.
--
-- \>\>\> rest <- S.print $ (\s -> splitsAt 2 s >>= splitsAt 2) each' [1..5]
-- 1
-- 2
-- 3
-- 4
-- \>\>\> S.print rest
-- 5
splitsAt ::
forall f m r.
(HasCallStack, Control.Monad m, Control.Functor f) =>
Int ->
Stream f m r %1 ->
Stream f m (Stream f m r)
splitsAt n stream = loop n stream
where
loop :: Int -> Stream f m r %1 -> Stream f m (Stream f m r)
loop n stream = case compare n 0 of
LT -> Prelude.error "splitsAt called with negative index" $ stream
EQ -> Return stream
GT ->
stream & \case
Return r -> Return $ Return r
Effect m -> Effect $ Control.fmap (loop n) m
Step f -> Step $ Control.fmap (loop (n - 1)) f
{-# INLINEABLE splitsAt #-}
-- | Break a stream into substreams each with n functorial layers.
--
-- \>\>\> S.print $ mapped S.sum $ chunksOf 2 $ each' [1,1,1,1,1]
-- 2
-- 2
-- 1
chunksOf ::
forall f m r.
(HasCallStack, Control.Monad m, Control.Functor f) =>
Int ->
Stream f m r %1 ->
Stream (Stream f m) m r
chunksOf n stream = loop n stream
where
loop :: Int -> Stream f m r %1 -> Stream (Stream f m) m r
loop _ (Return r) = Return r
loop n stream = Step $ Control.fmap (loop n) $ splitsAt n stream
{-# INLINEABLE chunksOf #-}
-- | Dissolves the segmentation into layers of @Stream f m@ layers.
concats ::
forall f m r.
(Control.Monad m, Control.Functor f) =>
Stream (Stream f m) m r %1 ->
Stream f m r
concats = loop
where
loop :: Stream (Stream f m) m r %1 -> Stream f m r
loop stream =
stream & \case
Return r -> Return r
Effect m -> Effect $ Control.fmap loop m
Step f -> Control.do
rest <- Control.fmap loop f
rest
{-# INLINE concats #-}
-- Note. To keep the monad of the stream a control monad, we need
-- `(t m)` to be a control monad, and hence `t` to be a control
-- monad transformer.
-- | Interpolate a layer at each segment. This specializes to e.g.
--
-- > intercalates :: Stream f m () -> Stream (Stream f m) m r %1-> Stream f m r
intercalates ::
forall t m r x.
(Control.Monad m, Control.Monad (t m), Control.MonadTrans t, Consumable x) =>
t m x ->
Stream (t m) m r %1 ->
t m r
intercalates sep = go0
where
go0 :: Stream (t m) m r %1 -> t m r
go0 f =
f & \case
Return r -> Control.return r
Effect m -> Control.lift m Control.>>= go0
Step fstr -> Control.do
f' <- fstr
go1 f'
go1 :: Stream (t m) m r %1 -> t m r
go1 f =
f & \case
Return r -> Control.return r
Effect m -> Control.lift m Control.>>= go1
Step fstr -> Control.do
x <- sep
Control.return $ consume x
f' <- fstr
go1 f'
{-# INLINEABLE intercalates #-}
-- # Zipping, unzipping, separating and unseparating streams
-------------------------------------------------------------------------------
unzips ::
forall f g m r.
(Control.Monad m, Control.Functor f, Control.Functor g) =>
Stream (Compose f g) m r %1 ->
Stream f (Stream g m) r
unzips str =
destroyExposed
str
(\(Compose fgstr) -> Step (Control.fmap (Effect . yields) fgstr))
(Effect . Control.lift)
Return
{-# INLINEABLE unzips #-}
-- | Given a stream on a sum of functors, make it a stream on the left functor,
-- with the streaming on the other functor as the governing monad. This is
-- useful for acting on one or the other functor with a fold, leaving the
-- other material for another treatment. It generalizes
-- 'Data.Either.partitionEithers', but actually streams properly.
--
-- \>\>\> let odd_even = S.maps (S.distinguish even) $ S.each' [1..10::Int]
-- \>\>\> :t separate odd_even
-- separate odd_even
-- :: Monad m => Stream (Of Int) (Stream (Of Int) m) ()
--
-- Now, for example, it is convenient to fold on the left and right values separately:
--
-- \>\>\> S.toList $ S.toList $ separate odd_even
-- [2,4,6,8,10] :> ([1,3,5,7,9] :> ())
--
--
-- Or we can write them to separate files or whatever:
--
-- \>\>\> S.writeFile "even.txt" . S.show $ S.writeFile "odd.txt" . S.show $ S.separate odd_even
-- \>\>\> :! cat even.txt
-- 2
-- 4
-- 6
-- 8
-- 10
-- \>\>\> :! cat odd.txt
-- 1
-- 3
-- 5
-- 7
-- 9
--
-- Of course, in the special case of @Stream (Of a) m r@, we can achieve the above
-- effects more simply by using 'Streaming.Prelude.copy'
--
-- \>\>\> S.toList . S.filter even $ S.toList . S.filter odd $ S.copy $ each [1..10::Int]
-- [2,4,6,8,10] :> ([1,3,5,7,9] :> ())
--
--
-- But 'separate' and 'unseparate' are functor-general.
separate ::
forall f g m r.
(Control.Monad m, Control.Functor f, Control.Functor g) =>
Stream (Sum f g) m r ->
Stream f (Stream g m) r
separate str = destroyExposed str construct (Effect . Control.lift) Return
where
construct :: Sum f g (Stream f (Stream g m) r) %1 -> Stream f (Stream g m) r
construct (InL fss) = Step fss
construct (InR gss) = Effect (yields gss)
{-# INLINEABLE separate #-}
unseparate ::
(Control.Monad m, Control.Functor f, Control.Functor g) =>
Stream f (Stream g m) r ->
Stream (Sum f g) m r
unseparate str =
destroyExposed
str
(Step . InL)
(Control.join . maps InR)
Return
{-# INLINEABLE unseparate #-}
-- | Rearrange a succession of layers of the form @Compose m (f x)@.
--
-- we could as well define @decompose@ by @mapsM@:
--
-- > decompose = mapped getCompose
--
-- but @mapped@ is best understood as:
--
-- > mapped phi = decompose . maps (Compose . phi)
--
-- since @maps@ and @hoist@ are the really fundamental operations that preserve the
-- shape of the stream:
--
-- > maps :: (Control.Monad m, Control.Functor f) => (forall x. f x %1-> g x) -> Stream f m r %1-> Stream g m r
-- > hoist :: (Control.Monad m, Control.Functor f) => (forall a. m a %1-> n a) -> Stream f m r %1-> Stream f n r
decompose ::
forall f m r.
(Control.Monad m, Control.Functor f) =>
Stream (Compose m f) m r %1 ->
Stream f m r
decompose = loop
where
loop :: Stream (Compose m f) m r %1 -> Stream f m r
loop stream =
stream & \case
Return r -> Return r
Effect m -> Effect $ Control.fmap loop m
Step (Compose mfs) -> Effect $ Control.do
fstream <- mfs
Control.return $ Step (Control.fmap loop fstream)
{-# INLINEABLE decompose #-}
-- Note. For 'loop' to recurse over functoral steps, it must be a
-- linear function, and hence, `ext` must be linear in its second argument.
-- Further, the first argument of `ext` ought to be a linear function,
-- because it is typically applied to the input stream in `ext`, and hence
-- should be linear.
-- | If 'Of' had a @Comonad@ instance, then we'd have
--
-- @copy = expand extend@
--
-- See 'expandPost' for a version that requires a @Control.Functor g@
-- instance instead.
expand ::
forall f m r g h.
(Control.Monad m, Control.Functor f) =>
(forall a b. (g a %1 -> b) -> f a %1 -> h b) ->
Stream f m r %1 ->
Stream g (Stream h m) r
expand ext = loop
where
loop :: Stream f m r %1 -> Stream g (Stream h m) r
loop (Return r) = Return r
loop (Step f) = Effect $ Step $ ext (Return . Step) (Control.fmap loop f)
loop (Effect m) = Effect $ Effect $ Control.fmap (Return . loop) m
{-# INLINEABLE expand #-}
-- See note on 'expand'.
-- | If 'Of' had a @Comonad@ instance, then we'd have
--
-- @copy = expandPost extend@
--
-- See 'expand' for a version that requires a @Control.Functor f@ instance
-- instead.
expandPost ::
forall f m r g h.
(Control.Monad m, Control.Functor g) =>
(forall a b. (g a %1 -> b) -> f a %1 -> h b) ->
Stream f m r %1 ->
Stream g (Stream h m) r
expandPost ext = loop
where
loop :: Stream f m r %1 -> Stream g (Stream h m) r
loop (Return r) = Return r
loop (Step f) = Effect $ Step $ ext (Return . Step . Control.fmap loop) f
loop (Effect m) = Effect $ Effect $ Control.fmap (Return . loop) m
{-# INLINEABLE expandPost #-}
-- # Eliminating a 'Stream'
-------------------------------------------------------------------------------
-- Note. Since the functor step is held linearly in the
-- 'Stream' datatype, the first argument must be a linear function
-- in order to linearly consume the 'Step' case of a stream.
-- | Map each layer to an effect, and run them all.
mapsM_ ::
(Control.Functor f, Control.Monad m) =>
(forall x. f x %1 -> m x) ->
Stream f m r %1 ->
m r
mapsM_ f = run . maps f
{-# INLINE mapsM_ #-}
-- | Run the effects in a stream that merely layers effects.
run :: (Control.Monad m) => Stream m m r %1 -> m r
run = loop
where
loop :: (Control.Monad m) => Stream m m r %1 -> m r
loop stream =
stream & \case
Return r -> Control.return r
Effect m -> m Control.>>= loop
Step mrest -> mrest Control.>>= loop
{-# INLINEABLE run #-}
-- | 'streamFold' reorders the arguments of 'destroy' to be more akin
-- to @foldr@ It is more convenient to query in ghci to figure out
-- what kind of \'algebra\' you need to write.
--
-- \>\>\> :t streamFold Control.return Control.join
-- (Control.Monad m, Control.Functor f) =>
-- (f (m a) %1-> m a) -> Stream f m a %1-> m a -- iterT
--
-- \>\>\> :t streamFold Control.return (Control.join . Control.lift)
-- (Control.Monad m, Control.Monad (t m), Control.Functor f, Control.MonadTrans t) =>
-- (f (t m a) %1-> t m a) -> Stream f m a %1-> t m a -- iterTM
--
-- \>\>\> :t streamFold Control.return effect
-- (Control.Monad m, Control.Functor f, Control.Functor g) =>
-- (f (Stream g m r) %1-> Stream g m r) -> Stream f m r %1-> Stream g m r
--
-- \>\>\> :t \f -> streamFold Control.return effect (wrap . f)
-- (Control.Monad m, Control.Functor f, Control.Functor g) =>
-- (f (Stream g m a) %1-> g (Stream g m a))
-- -> Stream f m a %1-> Stream g m a -- maps
--
-- \>\>\> :t \f -> streamFold Control.return effect (effect . Control.fmap wrap . f)
-- (Control.Monad m, Control.Functor f, Control.Functor g) =>
-- (f (Stream g m a) %1-> m (g (Stream g m a)))
-- -> Stream f m a %1-> Stream g m a -- mapped
--
-- @
-- streamFold done eff construct
-- = eff . iterT (Control.return . construct . Control.fmap eff) . Control.fmap done
-- @
streamFold ::
(Control.Functor f, Control.Monad m) =>
(r %1 -> b) ->
(m b %1 -> b) ->
(f b %1 -> b) ->
Stream f m r %1 ->
b
streamFold done theEffect construct stream =
destroy stream construct theEffect done
{-# INLINE streamFold #-}
-- | Specialized fold following the usage of @Control.Monad.Trans.Free@
--
-- > iterT alg = streamFold Control.return Control.join alg
-- > iterT alg = runIdentityT . iterTM (IdentityT . alg . Control.fmap runIdentityT)
iterT ::
(Control.Functor f, Control.Monad m) =>
(f (m a) %1 -> m a) ->
Stream f m a %1 ->
m a
iterT out stream = destroyExposed stream out Control.join Control.return
{-# INLINE iterT #-}
-- | Specialized fold following the usage of @Control.Monad.Trans.Free@
--
-- > iterTM alg = streamFold Control.return (Control.join . Control.lift)
-- > iterTM alg = iterT alg . hoist Control.lift
iterTM ::
( Control.Functor f,
Control.Monad m,
Control.MonadTrans t,
Control.Monad (t m)
) =>
(f (t m a) %1 -> t m a) ->
Stream f m a %1 ->
t m a
iterTM out stream =
destroyExposed stream out (Control.join . Control.lift) Control.return
{-# INLINE iterTM #-}
-- Note. 'destroy' needs to use linear functions in its church encoding
-- to consume the stream linearly.
-- | Map a stream to its church encoding; compare @Data.List.foldr@.
-- 'destroyExposed' may be more efficient in some cases when
-- applicable, but it is less safe.
--
-- @
-- destroy s construct eff done
-- = eff .
-- iterT (Control.return . construct . Control.fmap eff) .
-- Control.fmap done $ s
-- @
destroy ::
forall f m r b.
(Control.Functor f, Control.Monad m) =>
Stream f m r %1 ->
(f b %1 -> b) ->
(m b %1 -> b) ->
(r %1 -> b) ->
b
destroy stream0 construct theEffect done = theEffect (loop stream0)
where
loop :: Stream f m r %1 -> m b
loop stream =
stream & \case
Return r -> Control.return $ done r
Effect m -> m Control.>>= loop
Step f -> Control.return $ construct $ Control.fmap (theEffect . loop) f
{-# INLINEABLE destroy #-}