{-# LANGUAGE
  BangPatterns,
  DataKinds,
  GADTs,
  KindSignatures,
  RankNTypes,
  ScopedTypeVariables,
  StandaloneKindSignatures,
  TypeOperators #-}

-- | = Coroutines: yield as an algebraic effect
--
-- == Iterators
--
-- A simple use case of coroutines is as an expressive way of defining iterators.
--
-- An iterator is just a program which yields values. The following example
-- yields the integers 1, 2, 3, 4.
--
-- @
-- range1to4 :: z :> zz => Handler (Coroutine Int ()) z -> Eff zz ()
-- range1to4 h = do
--   'yield' h 1
--   'yield' h 2
--   'yield' h 3
--   'yield' h 4
-- @
--
-- The 'forCoroutine' handler is a "for" loop over an iterator,
-- running the loop body for every yielded element.
-- Here we collect the even values into a list stored in mutable @State@.
--
-- @
-- filterEven :: z :> zz => Handler (State [Int]) z -> Eff zz ()
-- filterEven h =
--   'forCoroutine' range1to4 \\n ->
--     if n \`mod\` 2 == 0
--     then modify h (n :)
--     else pure ()
--
-- filterEvenResult :: [Int]
-- filterEvenResult = runPureEff $ execState [] filterEven
--
-- -- 1 and 3 are filtered out, 2 and 4 are pushed into the queue
-- in that order, so they appear in reverse order.
-- -- filterEvenResult == [4,2]
-- @
--
-- == Cooperative concurrency
--
-- Coroutines are "cooperative threads", passing control to other coroutines
-- with explicit 'yield' calls.
--
-- In the following example, two threads yield a string back and forth,
-- appending a suffix every time.
--
-- @
-- pingpong :: Eff ss String
-- pingpong = 'withCoroutine' coThread mainThread
--   where
--     coThread z0 h = do
--       z1 <- 'yield' h (z0 ++ "pong")
--       z2 <- 'yield' h (z1 ++ "dong")
--       'yield' h (z2 ++ "bong")
--     mainThread h = do
--       s1 <- 'yield' h "ping"
--       s2 <- 'yield' h (s1 ++ "ding")
--       s3 <- 'yield' h (s2 ++ "bing")
--       pure s3
--
-- -- runPureEff pingpong == "pingpongdingdongbingbong"
-- @
--
-- More than two coroutines may be interleaved. In the snippet below, four
-- users pass a string to each other, extending it with breadcrumbs each time.
--
-- For example, @userLL@ sends a string to @userLR@ (identified using the
-- @Left (Right _)@ constructors in the 'yield' argument). When @userLL@
-- receives a second string @s'@ (from anywhere, in this case it will come from
-- @userRR@), it forwards it to @userRL@.
--
-- @
-- echo :: Eff ss String
-- echo = 'loopCoPipe' ((userLL |+ userLR) |+ (userRL |+ userRR)) (Left (Left \"S\"))
--   where
--     userLL = 'toCoPipe' \\s h -> do
--       s' <- 'yield' h (Left (Right (s ++ "-LL")))  -- send to userLR
--       'yield' h (Right (Left (s' ++ "-LL")))       -- send to userRL
--     userLR = 'toCoPipe' \\s h -> do
--       s' <- 'yield' h (Right (Left (s ++ "-LR")))  -- send to userRL
--       'yield' h (Right (Right (s' ++ "-LR")))      -- send to userRR
--     userRL = 'toCoPipe' \\s h -> do
--       s' <- 'yield' h (Right (Right (s ++ "-RL"))) -- send to userRR
--       'yield' h (Left (Right (s' ++ "-RL")))       -- send to userLR
--     userRR = 'toCoPipe' \\s h -> do
--       s' <- 'yield' h (Left (Left (s ++ "-RR")))   -- send to userLL
--       pure (s' ++ "-RR")                           -- terminate
--     (|+) = 'eitherCoPipe' id
--
-- -- runPureEff echo == "S-LL-LR-RL-RR-LL-RL-LR-RR"
-- @
--
-- == References
--
-- Coroutines are also known as generators in Javascript and Python.
--
-- - <https://en.wikipedia.org/wiki/Coroutine Coroutine> and
--   <https://en.wikipedia.org/wiki/Generator_(computer_programming) Generator>
--   on Wikipedia
-- - <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#description Generators in Javascript>
-- - <https://docs.python.org/3/reference/expressions.html#yieldexpr Generators in Python>
module Bluefin.Algae.Coroutine
  ( -- * Coroutines

    -- ** Operations
    Coroutine(..)
  , yield

    -- ** Handlers
  , withCoroutine
  , forCoroutine

    -- * Functions
  , (:->)
  , apply
  , withFunction

    -- * Pipes
    -- ** Definition
  , Pipe(..)
  , PipeEvent(..)
  , CoPipe(..)

    -- ** Unwrap
  , stepPipe
  , applyCoPipe
  , next

    -- ** Constructors
  , simpleCoPipe
  , voidCoPipe
  , nothingPipe
  , nothingCoPipe

    -- ** Pipe combinators
  , mapPipe
  , mapCoPipe
  , eitherPipe
  , eitherCoPipe
  , openPipe
  , openCoPipe

    -- ** Destructors
  , runPipe
  , runCoPipe
  , forPipe
  , forCoPipe
  , loopPipe
  , loopCoPipe
  , feedPipe
  , feedCoPipe

    -- ** Handlers involving pipes

    -- | Using the handlers 'toCoPipe' and 'toPipe' as primitives,
    -- we can define the other handlers.
    --
    -- @
    -- 'withCoroutine' g f = 'runPipe' ('toCoPipe' g) ('toPipe' f)
    -- 'forCoroutine' g f = 'runPipe' ('simpleCoPipe' g) ('toPipe' f)
    -- 'withCoPipe' g f = 'runPipe' g ('toPipe' f)
    -- @
  , CoPipeSEff
  , toCoPipe
  , PipeSEff
  , toPipe
  , withCoPipe

    -- ** Interpreting pipes as coroutines
  , CoPipeEff
  , fromCoPipe
  , PipeEff
  , fromPipe
  ) where

import Data.Coerce (coerce)
import Data.Function (fix)
import Data.Functor ((<&>))
import Data.Kind (Type)
import Data.Void (Void, absurd)
import Bluefin.Eff
import Bluefin.Algae

-- * Coroutines

-- | Coroutine effect with outputs @o@ and inputs @i@.
data Coroutine o i :: AEffect where
  -- | Yield an output and wait for an input.
  Yield :: o -> Coroutine o i i

-- | Call the 'Yield' operation.
yield :: z :> zz => Handler (Coroutine o i) z -> o -> Eff zz i
yield :: forall (z :: Effects) (zz :: Effects) o i.
(z :> zz) =>
Handler (Coroutine o i) z -> o -> Eff zz i
yield Handler (Coroutine o i) z
h o
o = Handler (Coroutine o i) z -> Coroutine o i i -> Eff zz i
forall (s :: Effects) (ss :: Effects) (f :: AEffect) a.
(s :> ss) =>
Handler f s -> f a -> Eff ss a
call Handler (Coroutine o i) z
h (o -> Coroutine o i i
forall o i. o -> Coroutine o i i
Yield o
o)

-- | This type synonym rebrands 'Coroutine' into a generic "function" effect,
-- since without the concurrency connotations, the 'Yield' operation looks
-- like a simple function call.
type (:->) :: Type -> Type -> AEffect
type (:->) = Coroutine

-- | Synonym for 'yield'.
apply :: z :> zz => Handler (a :-> b) z -> a -> Eff zz b
apply :: forall (z :: Effects) (zz :: Effects) o i.
(z :> zz) =>
Handler (Coroutine o i) z -> o -> Eff zz i
apply = Handler (Coroutine a b) z -> a -> Eff zz b
forall (z :: Effects) (zz :: Effects) o i.
(z :> zz) =>
Handler (Coroutine o i) z -> o -> Eff zz i
yield

-- | Interpret @(':->')@ with a function.
withFunction :: forall a b r zz.
  (a -> Eff zz b) ->
  ScopedEff (a :-> b) zz r ->
  Eff zz r
withFunction :: forall a b r (zz :: Effects).
(a -> Eff zz b) -> ScopedEff (a :-> b) zz r -> Eff zz r
withFunction a -> Eff zz b
f ScopedEff (a :-> b) zz r
g = ScopedEff (a :-> b) zz r -> (a -> Eff zz b) -> Eff zz r
forall o i a (zz :: Effects).
ScopedEff (Coroutine o i) zz a -> (o -> Eff zz i) -> Eff zz a
forCoroutine Handler (a :-> b) s -> Eff (s :& zz) r
ScopedEff (a :-> b) zz r
g a -> Eff zz b
f
-- This is morally @flip forCoroutine@ except that it wouldn't type check
-- because 'forCoroutine' has a higher-rank type.

-- * Pipes

-- | Output-first coroutine.
--
-- A 'Pipe' represents a coroutine as a state machine:
-- a 'Pipe' yields an output @o@ and waits for an input @i@, or terminates with
-- a result @a@.
--
-- @
-- +--------------+                  +----------------+
-- | 'Pipe' i o m a | ('Yielding' o)---> | 'CoPipe' i o m a |
-- |              | <------(input i) |                |
-- +--------------+                  +----------------+
--        v ('Done')
--      +---+
--      | a |
--      +---+
-- @
newtype Pipe i o m a = MkPipe (m (PipeEvent i o m a))

-- | Events of 'Pipe'.
data PipeEvent i o m a
  = Done a                       -- ^ Final result @a@
  | Yielding o (CoPipe i o m a)  -- ^ Output @o@ and continue as 'CoPipe'.

-- | Input-first coroutine. 'Pipe' continuation.
newtype CoPipe i o m a
  = MkCoPipe (i -> Pipe i o m a)  -- ^ Input @i@ and continue as 'Pipe'.

-- | Unwrap 'Pipe'.
stepPipe :: Pipe i o m a -> m (PipeEvent i o m a)
stepPipe :: forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe (MkPipe m (PipeEvent i o m a)
p) = m (PipeEvent i o m a)
p

-- | Unwrap 'CoPipe'.
applyCoPipe :: CoPipe i o m a -> i -> Pipe i o m a
applyCoPipe :: forall i o (m :: AEffect) a. CoPipe i o m a -> i -> Pipe i o m a
applyCoPipe (MkCoPipe i -> Pipe i o m a
k) = i -> Pipe i o m a
k

-- | Apply a non-returning 'CoPipe' to yield the next output and 'CoPipe' state.
next :: Functor m => CoPipe i o m Void -> i -> m (o, CoPipe i o m Void)
next :: forall (m :: AEffect) i o.
Functor m =>
CoPipe i o m Void -> i -> m (o, CoPipe i o m Void)
next (MkCoPipe i -> Pipe i o m Void
f) i
i = PipeEvent i o m Void -> (o, CoPipe i o m Void)
forall {i} {a} {m :: AEffect}.
PipeEvent i a m Void -> (a, CoPipe i a m Void)
go (PipeEvent i o m Void -> (o, CoPipe i o m Void))
-> m (PipeEvent i o m Void) -> m (o, CoPipe i o m Void)
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
<$> Pipe i o m Void -> m (PipeEvent i o m Void)
forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe (i -> Pipe i o m Void
f i
i) where
  go :: PipeEvent i a m Void -> (a, CoPipe i a m Void)
go (Done Void
v) = Void -> (a, CoPipe i a m Void)
forall a. Void -> a
absurd Void
v
  go (Yielding a
o CoPipe i a m Void
k) = (a
o, CoPipe i a m Void
k)

-- | A 'CoPipe' which runs the same function on every input.
simpleCoPipe :: Functor m => (i -> m o) -> CoPipe i o m void
simpleCoPipe :: forall (m :: AEffect) i o void.
Functor m =>
(i -> m o) -> CoPipe i o m void
simpleCoPipe i -> m o
f = (CoPipe i o m void -> CoPipe i o m void) -> CoPipe i o m void
forall a. (a -> a) -> a
fix ((CoPipe i o m void -> CoPipe i o m void) -> CoPipe i o m void)
-> (CoPipe i o m void -> CoPipe i o m void) -> CoPipe i o m void
forall a b. (a -> b) -> a -> b
$ \CoPipe i o m void
self -> (i -> Pipe i o m void) -> CoPipe i o m void
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe (\i
i -> m (PipeEvent i o m void) -> Pipe i o m void
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe ((\o
o -> o -> CoPipe i o m void -> PipeEvent i o m void
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding o
o CoPipe i o m void
self) (o -> PipeEvent i o m void) -> m o -> m (PipeEvent i o m void)
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
<$> i -> m o
f i
i))

-- | Transform inputs and outputs of a 'Pipe'.
mapPipe :: Functor m => (i' -> i) -> (o -> o') -> (a -> a') -> Pipe i o m a -> Pipe i' o' m a'
mapPipe :: forall (m :: AEffect) i' i o o' a a'.
Functor m =>
(i' -> i)
-> (o -> o') -> (a -> a') -> Pipe i o m a -> Pipe i' o' m a'
mapPipe i' -> i
fi o -> o'
fo a -> a'
fa = Pipe i o m a -> Pipe i' o' m a'
mapPipe_
  where
    mapPipe_ :: Pipe i o m a -> Pipe i' o' m a'
mapPipe_ (MkPipe m (PipeEvent i o m a)
p) = m (PipeEvent i' o' m a') -> Pipe i' o' m a'
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe (PipeEvent i o m a -> PipeEvent i' o' m a'
loop (PipeEvent i o m a -> PipeEvent i' o' m a')
-> m (PipeEvent i o m a) -> m (PipeEvent i' o' m a')
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
<$> m (PipeEvent i o m a)
p)
    loop :: PipeEvent i o m a -> PipeEvent i' o' m a'
loop (Done a
a) = a' -> PipeEvent i' o' m a'
forall i o (m :: AEffect) a. a -> PipeEvent i o m a
Done (a -> a'
fa a
a)
    loop (Yielding o
o CoPipe i o m a
k) = o' -> CoPipe i' o' m a' -> PipeEvent i' o' m a'
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding (o -> o'
fo o
o) (CoPipe i o m a -> CoPipe i' o' m a'
mapCoPipe_ CoPipe i o m a
k)
    mapCoPipe_ :: CoPipe i o m a -> CoPipe i' o' m a'
mapCoPipe_ (MkCoPipe i -> Pipe i o m a
k) = (i' -> Pipe i' o' m a') -> CoPipe i' o' m a'
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe (Pipe i o m a -> Pipe i' o' m a'
mapPipe_ (Pipe i o m a -> Pipe i' o' m a')
-> (i' -> Pipe i o m a) -> i' -> Pipe i' o' m a'
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> Pipe i o m a
k (i -> Pipe i o m a) -> (i' -> i) -> i' -> Pipe i o m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i' -> i
fi)

-- | Transform the input and output of a 'CoPipe'.
mapCoPipe :: Functor m => (i' -> i) -> (o -> o') -> (a -> a') -> CoPipe i o m a -> CoPipe i' o' m a'
mapCoPipe :: forall (m :: AEffect) i' i o o' a a'.
Functor m =>
(i' -> i)
-> (o -> o') -> (a -> a') -> CoPipe i o m a -> CoPipe i' o' m a'
mapCoPipe i' -> i
fi o -> o'
fo a -> a'
fa (MkCoPipe i -> Pipe i o m a
k) = (i' -> Pipe i' o' m a') -> CoPipe i' o' m a'
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe ((i' -> i)
-> (o -> o') -> (a -> a') -> Pipe i o m a -> Pipe i' o' m a'
forall (m :: AEffect) i' i o o' a a'.
Functor m =>
(i' -> i)
-> (o -> o') -> (a -> a') -> Pipe i o m a -> Pipe i' o' m a'
mapPipe i' -> i
fi o -> o'
fo a -> a'
fa (Pipe i o m a -> Pipe i' o' m a')
-> (i' -> Pipe i o m a) -> i' -> Pipe i' o' m a'
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> Pipe i o m a
k (i -> Pipe i o m a) -> (i' -> i) -> i' -> Pipe i o m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i' -> i
fi)

-- | Run a 'Pipe' with a 'CoPipe' to respond to every output.
runPipe :: Monad m => CoPipe i o m Void -> Pipe o i m a -> m a
runPipe :: forall (m :: AEffect) i o a.
Monad m =>
CoPipe i o m Void -> Pipe o i m a -> m a
runPipe CoPipe i o m Void
t (MkPipe m (PipeEvent o i m a)
p) = m (PipeEvent o i m a)
p m (PipeEvent o i m a) -> (PipeEvent o i m a -> m a) -> m a
forall a b. m a -> (a -> m b) -> m b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \PipeEvent o i m a
e -> case PipeEvent o i m a
e of
  Done a
a -> a -> m a
forall a. a -> m a
forall (f :: AEffect) a. Applicative f => a -> f a
pure a
a
  Yielding i
i CoPipe o i m a
k -> do
    (o
o, CoPipe i o m Void
t') <- CoPipe i o m Void -> i -> m (o, CoPipe i o m Void)
forall (m :: AEffect) i o.
Functor m =>
CoPipe i o m Void -> i -> m (o, CoPipe i o m Void)
next CoPipe i o m Void
t i
i
    CoPipe i o m Void -> CoPipe o i m a -> o -> m a
forall (m :: AEffect) i o a.
Monad m =>
CoPipe i o m Void -> CoPipe o i m a -> o -> m a
runCoPipe CoPipe i o m Void
t' CoPipe o i m a
k o
o

-- | Run a 'CoPipe' with another 'CoPipe' to respond to every input.
runCoPipe :: Monad m => CoPipe i o m Void -> CoPipe o i m a -> o -> m a
runCoPipe :: forall (m :: AEffect) i o a.
Monad m =>
CoPipe i o m Void -> CoPipe o i m a -> o -> m a
runCoPipe CoPipe i o m Void
t (MkCoPipe o -> Pipe o i m a
k) o
i = CoPipe i o m Void -> Pipe o i m a -> m a
forall (m :: AEffect) i o a.
Monad m =>
CoPipe i o m Void -> Pipe o i m a -> m a
runPipe CoPipe i o m Void
t (o -> Pipe o i m a
k o
i)

-- | Iterate through a 'Pipe'. Respond to every 'Yielding' event by running the loop body.
-- Return the final result of the 'Pipe'.
--
-- @
-- 'forPipe' p g = 'runPipe' ('simpleCoPipe' g) p
-- @
forPipe :: Monad m =>
  Pipe i o m a ->  -- ^ Iterator
  (o -> m i) ->    -- ^ Loop body
  m a
forPipe :: forall (m :: AEffect) i o a.
Monad m =>
Pipe i o m a -> (o -> m i) -> m a
forPipe Pipe i o m a
p o -> m i
h = Pipe i o m a -> m (PipeEvent i o m a)
forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe Pipe i o m a
p m (PipeEvent i o m a) -> (PipeEvent i o m a -> m a) -> m a
forall a b. m a -> (a -> m b) -> m b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= PipeEvent i o m a -> m a
loop
  where
    loop :: PipeEvent i o m a -> m a
loop (Done a
a) = a -> m a
forall a. a -> m a
forall (f :: AEffect) a. Applicative f => a -> f a
pure a
a
    loop (Yielding o
o CoPipe i o m a
k) = o -> m i
h o
o m i -> (i -> m a) -> m a
forall a b. m a -> (a -> m b) -> m b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \i
i -> Pipe i o m a -> m (PipeEvent i o m a)
forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe (CoPipe i o m a -> i -> Pipe i o m a
forall i o (m :: AEffect) a. CoPipe i o m a -> i -> Pipe i o m a
applyCoPipe CoPipe i o m a
k i
i) m (PipeEvent i o m a) -> (PipeEvent i o m a -> m a) -> m a
forall a b. m a -> (a -> m b) -> m b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= PipeEvent i o m a -> m a
loop

-- | Iterate through a 'CoPipe'.
forCoPipe :: Monad m =>
  CoPipe i o m a ->
  (o -> m i) ->
  i -> m a
forCoPipe :: forall (m :: AEffect) i o a.
Monad m =>
CoPipe i o m a -> (o -> m i) -> i -> m a
forCoPipe (MkCoPipe i -> Pipe i o m a
k) o -> m i
h i
i = Pipe i o m a -> (o -> m i) -> m a
forall (m :: AEffect) i o a.
Monad m =>
Pipe i o m a -> (o -> m i) -> m a
forPipe (i -> Pipe i o m a
k i
i) o -> m i
h

-- | 'CoPipe' with no input.
voidCoPipe :: CoPipe Void o m a
voidCoPipe :: forall o (m :: AEffect) a. CoPipe Void o m a
voidCoPipe = (Void -> Pipe Void o m a) -> CoPipe Void o m a
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe Void -> Pipe Void o m a
forall a. Void -> a
absurd

-- | Sum a copipe and a pipe with the same output type,
-- branching on the input type.
eitherPipe :: Monad m =>
  (i -> Either i1 i2) ->   -- ^ Dispatch input
  CoPipe i1 o m a ->       -- ^ Left copipe
  Pipe i2 o m a ->         -- ^ Right pipe
  Pipe i o m a
eitherPipe :: forall (m :: AEffect) i i1 i2 o a.
Monad m =>
(i -> Either i1 i2)
-> CoPipe i1 o m a -> Pipe i2 o m a -> Pipe i o m a
eitherPipe i -> Either i1 i2
split CoPipe i1 o m a
t0 (MkPipe m (PipeEvent i2 o m a)
p) = m (PipeEvent i o m a) -> Pipe i o m a
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe (m (PipeEvent i o m a) -> Pipe i o m a)
-> m (PipeEvent i o m a) -> Pipe i o m a
forall a b. (a -> b) -> a -> b
$ m (PipeEvent i2 o m a)
p m (PipeEvent i2 o m a)
-> (PipeEvent i2 o m a -> PipeEvent i o m a)
-> m (PipeEvent i o m a)
forall (f :: AEffect) a b. Functor f => f a -> (a -> b) -> f b
<&> \PipeEvent i2 o m a
e -> case PipeEvent i2 o m a
e of
  Done a
a -> a -> PipeEvent i o m a
forall i o (m :: AEffect) a. a -> PipeEvent i o m a
Done a
a
  Yielding o
o CoPipe i2 o m a
k -> o -> CoPipe i o m a -> PipeEvent i o m a
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding o
o ((i -> Either i1 i2)
-> CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
forall (m :: AEffect) i i1 i2 o a.
Functor m =>
(i -> Either i1 i2)
-> CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
eitherCoPipe i -> Either i1 i2
split CoPipe i1 o m a
t0 CoPipe i2 o m a
k)

-- | Sum two copipes with the same output type, branching on the input type.
eitherCoPipe :: Functor m =>
  (i -> Either i1 i2) ->   -- ^ Dispatch input
  CoPipe i1 o m a ->       -- ^ Left copipe
  CoPipe i2 o m a ->       -- ^ Right copipe
  CoPipe i o m a
eitherCoPipe :: forall (m :: AEffect) i i1 i2 o a.
Functor m =>
(i -> Either i1 i2)
-> CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
eitherCoPipe i -> Either i1 i2
split = CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
loop
  where
    loop :: CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
loop CoPipe i1 o m a
t1 CoPipe i2 o m a
t2 = (i -> Pipe i o m a) -> CoPipe i o m a
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe (m (PipeEvent i o m a) -> Pipe i o m a
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe (m (PipeEvent i o m a) -> Pipe i o m a)
-> (i -> m (PipeEvent i o m a)) -> i -> Pipe i o m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CoPipe i1 o m a
-> CoPipe i2 o m a -> Either i1 i2 -> m (PipeEvent i o m a)
transduce_ CoPipe i1 o m a
t1 CoPipe i2 o m a
t2 (Either i1 i2 -> m (PipeEvent i o m a))
-> (i -> Either i1 i2) -> i -> m (PipeEvent i o m a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> Either i1 i2
split)
    transduce_ :: CoPipe i1 o m a
-> CoPipe i2 o m a -> Either i1 i2 -> m (PipeEvent i o m a)
transduce_ (MkCoPipe i1 -> Pipe i1 o m a
t1) CoPipe i2 o m a
t2 (Left i1
i1) = Pipe i1 o m a -> m (PipeEvent i1 o m a)
forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe (i1 -> Pipe i1 o m a
t1 i1
i1) m (PipeEvent i1 o m a)
-> (PipeEvent i1 o m a -> PipeEvent i o m a)
-> m (PipeEvent i o m a)
forall (f :: AEffect) a b. Functor f => f a -> (a -> b) -> f b
<&> \PipeEvent i1 o m a
e -> case PipeEvent i1 o m a
e of
      Done a
a -> a -> PipeEvent i o m a
forall i o (m :: AEffect) a. a -> PipeEvent i o m a
Done a
a
      Yielding o
o CoPipe i1 o m a
t1' -> o -> CoPipe i o m a -> PipeEvent i o m a
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding o
o (CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
loop CoPipe i1 o m a
t1' CoPipe i2 o m a
t2)
    transduce_ CoPipe i1 o m a
t1 (MkCoPipe i2 -> Pipe i2 o m a
t2) (Right i2
i2) = Pipe i2 o m a -> m (PipeEvent i2 o m a)
forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe (i2 -> Pipe i2 o m a
t2 i2
i2) m (PipeEvent i2 o m a)
-> (PipeEvent i2 o m a -> PipeEvent i o m a)
-> m (PipeEvent i o m a)
forall (f :: AEffect) a b. Functor f => f a -> (a -> b) -> f b
<&> \PipeEvent i2 o m a
e -> case PipeEvent i2 o m a
e of
      Done a
a -> a -> PipeEvent i o m a
forall i o (m :: AEffect) a. a -> PipeEvent i o m a
Done a
a
      Yielding o
o CoPipe i2 o m a
t2' -> o -> CoPipe i o m a -> PipeEvent i o m a
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding  o
o (CoPipe i1 o m a -> CoPipe i2 o m a -> CoPipe i o m a
loop CoPipe i1 o m a
t1 CoPipe i2 o m a
t2')

-- | Loop the output of a pipe back to its input.
loopPipe :: Monad m => Pipe o o m a -> m a
loopPipe :: forall (m :: AEffect) o a. Monad m => Pipe o o m a -> m a
loopPipe (MkPipe m (PipeEvent o o m a)
p) = m (PipeEvent o o m a)
p m (PipeEvent o o m a) -> (PipeEvent o o m a -> m a) -> m a
forall a b. m a -> (a -> m b) -> m b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \PipeEvent o o m a
e -> case PipeEvent o o m a
e of
  Done a
a -> a -> m a
forall a. a -> m a
forall (f :: AEffect) a. Applicative f => a -> f a
pure a
a
  Yielding o
o CoPipe o o m a
k -> CoPipe o o m a -> o -> m a
forall (m :: AEffect) o a. Monad m => CoPipe o o m a -> o -> m a
loopCoPipe CoPipe o o m a
k o
o

-- | Forward the output of a 'CoPipe' to its input.
loopCoPipe :: Monad m => CoPipe o o m a -> o -> m a
loopCoPipe :: forall (m :: AEffect) o a. Monad m => CoPipe o o m a -> o -> m a
loopCoPipe (MkCoPipe o -> Pipe o o m a
k) o
o = Pipe o o m a -> m a
forall (m :: AEffect) o a. Monad m => Pipe o o m a -> m a
loopPipe (o -> Pipe o o m a
k o
o)

-- | Convert a returning 'Pipe' into a non-returning 'CoPipe',
-- yielding 'Nothing' forever once the end has been reached.
openPipe :: Applicative m => Pipe i o m () -> Pipe i (Maybe o) m void
openPipe :: forall (m :: AEffect) i o void.
Applicative m =>
Pipe i o m () -> Pipe i (Maybe o) m void
openPipe (MkPipe m (PipeEvent i o m ())
p) = m (PipeEvent i (Maybe o) m void) -> Pipe i (Maybe o) m void
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe (m (PipeEvent i o m ())
p m (PipeEvent i o m ())
-> (PipeEvent i o m () -> PipeEvent i (Maybe o) m void)
-> m (PipeEvent i (Maybe o) m void)
forall (f :: AEffect) a b. Functor f => f a -> (a -> b) -> f b
<&> \PipeEvent i o m ()
e -> case PipeEvent i o m ()
e of
  Done ()
_ -> Maybe o
-> CoPipe i (Maybe o) m void -> PipeEvent i (Maybe o) m void
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding Maybe o
forall a. Maybe a
Nothing CoPipe i (Maybe o) m void
forall (m :: AEffect) i o void.
Applicative m =>
CoPipe i (Maybe o) m void
nothingCoPipe
  Yielding o
o CoPipe i o m ()
k -> Maybe o
-> CoPipe i (Maybe o) m void -> PipeEvent i (Maybe o) m void
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding (o -> Maybe o
forall a. a -> Maybe a
Just o
o) (CoPipe i o m () -> CoPipe i (Maybe o) m void
forall (m :: AEffect) i o void.
Applicative m =>
CoPipe i o m () -> CoPipe i (Maybe o) m void
openCoPipe CoPipe i o m ()
k))

-- | Convert a returning 'CoPipe' into a non-returning 'CoPipe',
-- yielding 'Nothing' forever once the end has been reached.
openCoPipe :: Applicative m => CoPipe i o m () -> CoPipe i (Maybe o) m void
openCoPipe :: forall (m :: AEffect) i o void.
Applicative m =>
CoPipe i o m () -> CoPipe i (Maybe o) m void
openCoPipe (MkCoPipe i -> Pipe i o m ()
k) = (i -> Pipe i (Maybe o) m void) -> CoPipe i (Maybe o) m void
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe (Pipe i o m () -> Pipe i (Maybe o) m void
forall (m :: AEffect) i o void.
Applicative m =>
Pipe i o m () -> Pipe i (Maybe o) m void
openPipe (Pipe i o m () -> Pipe i (Maybe o) m void)
-> (i -> Pipe i o m ()) -> i -> Pipe i (Maybe o) m void
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> Pipe i o m ()
k)

-- | Yield 'Nothing' forever.
nothingPipe :: Applicative m => Pipe i (Maybe o) m void
nothingPipe :: forall (m :: AEffect) i o void.
Applicative m =>
Pipe i (Maybe o) m void
nothingPipe = m (PipeEvent i (Maybe o) m void) -> Pipe i (Maybe o) m void
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe (PipeEvent i (Maybe o) m void -> m (PipeEvent i (Maybe o) m void)
forall a. a -> m a
forall (f :: AEffect) a. Applicative f => a -> f a
pure (Maybe o
-> CoPipe i (Maybe o) m void -> PipeEvent i (Maybe o) m void
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding Maybe o
forall a. Maybe a
Nothing CoPipe i (Maybe o) m void
forall (m :: AEffect) i o void.
Applicative m =>
CoPipe i (Maybe o) m void
nothingCoPipe))

-- | Yield 'Nothing' forever.
nothingCoPipe :: Applicative m => CoPipe i (Maybe o) m void
nothingCoPipe :: forall (m :: AEffect) i o void.
Applicative m =>
CoPipe i (Maybe o) m void
nothingCoPipe = (i -> Pipe i (Maybe o) m void) -> CoPipe i (Maybe o) m void
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe (\i
_ -> Pipe i (Maybe o) m void
forall (m :: AEffect) i o void.
Applicative m =>
Pipe i (Maybe o) m void
nothingPipe)

-- | Representation of 'Pipe' as scoped 'Eff' computations.
type PipeSEff i o zz a = ScopedEff (Coroutine o i) zz a

-- | Representation of 'Pipe' as 'Eff' computations.
type PipeEff i o zz a = forall z. z :> zz => Handler (Coroutine o i) z -> Eff zz a

-- | Representation of 'CoPipe' as scoped 'Eff' computations.
type CoPipeSEff i o zz a = i -> ScopedEff (Coroutine o i) zz a

-- | Representation of 'CoPipe' as 'Eff' computations.
type CoPipeEff i o zz a = forall z. z :> zz => i -> Handler (Coroutine o i) z -> Eff zz a

-- | Run a 'Pipe' with a fixed number of inputs.
feedPipe :: Monad m => [i] -> Pipe i o m a -> m [o]
feedPipe :: forall (m :: AEffect) i o a.
Monad m =>
[i] -> Pipe i o m a -> m [o]
feedPipe [i]
is (MkPipe m (PipeEvent i o m a)
m) = m (PipeEvent i o m a)
m m (PipeEvent i o m a) -> (PipeEvent i o m a -> m [o]) -> m [o]
forall a b. m a -> (a -> m b) -> m b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \PipeEvent i o m a
e -> case PipeEvent i o m a
e of
  Done a
_ -> [o] -> m [o]
forall a. a -> m a
forall (f :: AEffect) a. Applicative f => a -> f a
pure []
  Yielding o
o CoPipe i o m a
k -> (o
o o -> [o] -> [o]
forall a. a -> [a] -> [a]
:) ([o] -> [o]) -> m [o] -> m [o]
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
<$> [i] -> CoPipe i o m a -> m [o]
forall (m :: AEffect) i o a.
Monad m =>
[i] -> CoPipe i o m a -> m [o]
feedCoPipe [i]
is CoPipe i o m a
k

-- | Run a 'CoPipe' with a fixed number of inputs.
feedCoPipe :: Monad m => [i] -> CoPipe i o m a -> m [o]
feedCoPipe :: forall (m :: AEffect) i o a.
Monad m =>
[i] -> CoPipe i o m a -> m [o]
feedCoPipe [] CoPipe i o m a
_ = [o] -> m [o]
forall a. a -> m a
forall (f :: AEffect) a. Applicative f => a -> f a
pure []
feedCoPipe (i
i : [i]
is) (MkCoPipe i -> Pipe i o m a
k) = [i] -> Pipe i o m a -> m [o]
forall (m :: AEffect) i o a.
Monad m =>
[i] -> Pipe i o m a -> m [o]
feedPipe [i]
is (i -> Pipe i o m a
k i
i)

-- * Handlers

-- | Convert a coroutine that doesn't return into a 'CoPipe'.
toCoPipe :: forall o i a zz.
  CoPipeSEff i o zz a -> CoPipe i o (Eff zz) a
toCoPipe :: forall o i a (zz :: Effects).
CoPipeSEff i o zz a -> CoPipe i o (Eff zz) a
toCoPipe CoPipeSEff i o zz a
f = (i -> Pipe i o (Eff zz) a) -> CoPipe i o (Eff zz) a
forall i o (m :: AEffect) a. (i -> Pipe i o m a) -> CoPipe i o m a
MkCoPipe (\i
i -> PipeSEff i o zz a -> Pipe i o (Eff zz) a
forall o i a (zz :: Effects).
PipeSEff i o zz a -> Pipe i o (Eff zz) a
toPipe (\Handler (Coroutine o i) s
h -> CoPipeSEff i o zz a
f i
i Handler (Coroutine o i) s
h))

-- | Convert a 'CoPipe' into a coroutine.
fromCoPipe :: CoPipe i o (Eff zz) a -> CoPipeEff i o zz a
fromCoPipe :: forall i o (zz :: Effects) a.
CoPipe i o (Eff zz) a -> CoPipeEff i o zz a
fromCoPipe (MkCoPipe i -> Pipe i o (Eff zz) a
k) i
i Handler (Coroutine o i) z
h = Pipe i o (Eff zz) a -> PipeEff i o zz a
forall i o (zz :: Effects) a.
Pipe i o (Eff zz) a -> PipeEff i o zz a
fromPipe (i -> Pipe i o (Eff zz) a
k i
i) Handler (Coroutine o i) z
h

-- | Evaluate a coroutine into a 'Pipe'.
toPipe :: forall o i a zz.
  PipeSEff i o zz a ->
  Pipe i o (Eff zz) a
toPipe :: forall o i a (zz :: Effects).
PipeSEff i o zz a -> Pipe i o (Eff zz) a
toPipe PipeSEff i o zz a
f = Eff zz (PipeEvent i o (Eff zz) a) -> Pipe i o (Eff zz) a
forall i o (m :: AEffect) a. m (PipeEvent i o m a) -> Pipe i o m a
MkPipe (HandlerBody (Coroutine o i) zz (PipeEvent i o (Eff zz) a)
-> ScopedEff (Coroutine o i) zz (PipeEvent i o (Eff zz) a)
-> Eff zz (PipeEvent i o (Eff zz) a)
forall (f :: AEffect) (ss :: Effects) a.
HandlerBody f ss a -> ScopedEff f ss a -> Eff ss a
handle Coroutine o i x
-> (x -> Eff zz (PipeEvent i o (Eff zz) a))
-> Eff zz (PipeEvent i o (Eff zz) a)
HandlerBody (Coroutine o i) zz (PipeEvent i o (Eff zz) a)
coroutineHandler (Eff (s :& zz) a -> Eff (s :& zz) (PipeEvent i o (Eff zz) a)
forall (z :: Effects).
Eff (z :& zz) a -> Eff (z :& zz) (PipeEvent i o (Eff zz) a)
wrap (Eff (s :& zz) a -> Eff (s :& zz) (PipeEvent i o (Eff zz) a))
-> (Handler (Coroutine o i) s -> Eff (s :& zz) a)
-> Handler (Coroutine o i) s
-> Eff (s :& zz) (PipeEvent i o (Eff zz) a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler (Coroutine o i) s -> Eff (s :& zz) a
PipeSEff i o zz a
f))
  where
    coroutineHandler :: HandlerBody (Coroutine o i) zz (PipeEvent i o (Eff zz) a)
    coroutineHandler :: HandlerBody (Coroutine o i) zz (PipeEvent i o (Eff zz) a)
coroutineHandler (Yield o
o) x -> Eff zz (PipeEvent i o (Eff zz) a)
k = PipeEvent i o (Eff zz) a -> Eff zz (PipeEvent i o (Eff zz) a)
forall a. a -> Eff zz a
forall (f :: AEffect) a. Applicative f => a -> f a
pure (o -> CoPipe i o (Eff zz) a -> PipeEvent i o (Eff zz) a
forall i o (m :: AEffect) a.
o -> CoPipe i o m a -> PipeEvent i o m a
Yielding o
o ((x -> Eff zz (PipeEvent i o (Eff zz) a)) -> CoPipe i o (Eff zz) a
forall a b. Coercible a b => a -> b
coerce x -> Eff zz (PipeEvent i o (Eff zz) a)
k))

    wrap :: Eff (z :& zz) a -> Eff (z :& zz) (PipeEvent i o (Eff zz) a)
    wrap :: forall (z :: Effects).
Eff (z :& zz) a -> Eff (z :& zz) (PipeEvent i o (Eff zz) a)
wrap = (a -> PipeEvent i o (Eff zz) a)
-> Eff (z :& zz) a -> Eff (z :& zz) (PipeEvent i o (Eff zz) a)
forall a b. (a -> b) -> Eff (z :& zz) a -> Eff (z :& zz) b
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> PipeEvent i o (Eff zz) a
forall i o (m :: AEffect) a. a -> PipeEvent i o m a
Done

-- | Convet a 'Pipe' into a coroutine.
fromPipe :: Pipe i o (Eff zz) a -> PipeEff i o zz a
fromPipe :: forall i o (zz :: Effects) a.
Pipe i o (Eff zz) a -> PipeEff i o zz a
fromPipe (MkPipe Eff zz (PipeEvent i o (Eff zz) a)
p) Handler (Coroutine o i) z
h = Eff zz (PipeEvent i o (Eff zz) a)
p Eff zz (PipeEvent i o (Eff zz) a)
-> (PipeEvent i o (Eff zz) a -> Eff zz a) -> Eff zz a
forall a b. Eff zz a -> (a -> Eff zz b) -> Eff zz b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \PipeEvent i o (Eff zz) a
e -> case PipeEvent i o (Eff zz) a
e of
  Done a
a -> a -> Eff zz a
forall a. a -> Eff zz a
forall (f :: AEffect) a. Applicative f => a -> f a
pure a
a
  Yielding o
o CoPipe i o (Eff zz) a
k -> Handler (Coroutine o i) z -> o -> Eff zz i
forall (z :: Effects) (zz :: Effects) o i.
(z :> zz) =>
Handler (Coroutine o i) z -> o -> Eff zz i
yield Handler (Coroutine o i) z
h o
o Eff zz i -> (i -> Eff zz a) -> Eff zz a
forall a b. Eff zz a -> (a -> Eff zz b) -> Eff zz b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \i
i -> CoPipe i o (Eff zz) a -> CoPipeEff i o zz a
forall i o (zz :: Effects) a.
CoPipe i o (Eff zz) a -> CoPipeEff i o zz a
fromCoPipe CoPipe i o (Eff zz) a
k i
i Handler (Coroutine o i) z
h

-- | Interleave the execution of a copipe and a coroutine.
withCoPipe :: forall o i a zz.
  CoPipe i o (Eff zz) a ->
  ScopedEff (Coroutine i o) zz a ->  -- ^ Starting coroutine
  Eff zz a
withCoPipe :: forall o i a (zz :: Effects).
CoPipe i o (Eff zz) a -> ScopedEff (Coroutine i o) zz a -> Eff zz a
withCoPipe CoPipe i o (Eff zz) a
g ScopedEff (Coroutine i o) zz a
f = CoPipe i o (Eff zz) a
-> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a) -> Eff zz a
forall g. g -> Eff zz (g -> Eff zz a) -> Eff zz a
with CoPipe i o (Eff zz) a
g (HandlerBody (Coroutine i o) zz (CoPipe i o (Eff zz) a -> Eff zz a)
-> ScopedEff (Coroutine i o) zz (CoPipe i o (Eff zz) a -> Eff zz a)
-> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a)
forall (f :: AEffect) (ss :: Effects) a.
HandlerBody f ss a -> ScopedEff f ss a -> Eff ss a
handle Coroutine i o x
-> (x -> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a))
-> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a)
HandlerBody (Coroutine i o) zz (CoPipe i o (Eff zz) a -> Eff zz a)
coroutineHandler ((a -> CoPipe i o (Eff zz) a -> Eff zz a)
-> Eff (s :& zz) a
-> Eff (s :& zz) (CoPipe i o (Eff zz) a -> Eff zz a)
forall a b. (a -> b) -> Eff (s :& zz) a -> Eff (s :& zz) b
forall (f :: AEffect) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> CoPipe i o (Eff zz) a -> Eff zz a
forall z. a -> z -> Eff zz a
wrap (Eff (s :& zz) a
 -> Eff (s :& zz) (CoPipe i o (Eff zz) a -> Eff zz a))
-> (Handler (Coroutine i o) s -> Eff (s :& zz) a)
-> Handler (Coroutine i o) s
-> Eff (s :& zz) (CoPipe i o (Eff zz) a -> Eff zz a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler (Coroutine i o) s -> Eff (s :& zz) a
ScopedEff (Coroutine i o) zz a
f))
  where
    coroutineHandler :: HandlerBody (Coroutine i o) zz (CoPipe i o (Eff zz) a -> Eff zz a)
    coroutineHandler :: HandlerBody (Coroutine i o) zz (CoPipe i o (Eff zz) a -> Eff zz a)
coroutineHandler (Yield i
o) x -> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a)
k = (CoPipe i o (Eff zz) a -> Eff zz a)
-> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a)
forall a. a -> Eff zz a
forall (f :: AEffect) a. Applicative f => a -> f a
pure ((CoPipe i o (Eff zz) a -> Eff zz a)
 -> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a))
-> (CoPipe i o (Eff zz) a -> Eff zz a)
-> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a)
forall a b. (a -> b) -> a -> b
$ \CoPipe i o (Eff zz) a
g1 -> do
      Pipe i o (Eff zz) a -> Eff zz (PipeEvent i o (Eff zz) a)
forall i o (m :: AEffect) a. Pipe i o m a -> m (PipeEvent i o m a)
stepPipe (CoPipe i o (Eff zz) a -> i -> Pipe i o (Eff zz) a
forall i o (m :: AEffect) a. CoPipe i o m a -> i -> Pipe i o m a
applyCoPipe CoPipe i o (Eff zz) a
g1 i
o) Eff zz (PipeEvent i o (Eff zz) a)
-> (PipeEvent i o (Eff zz) a -> Eff zz a) -> Eff zz a
forall a b. Eff zz a -> (a -> Eff zz b) -> Eff zz b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \PipeEvent i o (Eff zz) a
e -> case PipeEvent i o (Eff zz) a
e of
        Done a
a -> a -> Eff zz a
forall a. a -> Eff zz a
forall (f :: AEffect) a. Applicative f => a -> f a
pure a
a
        Yielding o
i CoPipe i o (Eff zz) a
g2 -> CoPipe i o (Eff zz) a
-> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a) -> Eff zz a
forall g. g -> Eff zz (g -> Eff zz a) -> Eff zz a
with CoPipe i o (Eff zz) a
g2 (x -> Eff zz (CoPipe i o (Eff zz) a -> Eff zz a)
k o
x
i)

    wrap :: a -> z -> Eff zz a
    wrap :: forall z. a -> z -> Eff zz a
wrap a
a z
_ = a -> Eff zz a
forall a. a -> Eff zz a
forall (f :: AEffect) a. Applicative f => a -> f a
pure a
a

    with :: forall g. g -> Eff zz (g -> Eff zz a) -> Eff zz a
    with :: forall g. g -> Eff zz (g -> Eff zz a) -> Eff zz a
with g
g' Eff zz (g -> Eff zz a)
m = Eff zz (g -> Eff zz a)
m Eff zz (g -> Eff zz a) -> ((g -> Eff zz a) -> Eff zz a) -> Eff zz a
forall a b. Eff zz a -> (a -> Eff zz b) -> Eff zz b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= \g -> Eff zz a
f' -> g -> Eff zz a
f' g
g'

-- | Interleave the execution of two coroutines, feeding each one's output to the other's input.
-- Return the result of the first thread to terminate (the other is discarded)
withCoroutine :: forall o i a zz.
  (i -> ScopedEff (Coroutine o i) zz a) ->
  ScopedEff (Coroutine i o) zz a ->         -- ^ Starting coroutine
  Eff zz a
withCoroutine :: forall o i a (zz :: Effects).
(i -> ScopedEff (Coroutine o i) zz a)
-> ScopedEff (Coroutine i o) zz a -> Eff zz a
withCoroutine i -> ScopedEff (Coroutine o i) zz a
g ScopedEff (Coroutine i o) zz a
f = CoPipe i o (Eff zz) a -> ScopedEff (Coroutine i o) zz a -> Eff zz a
forall o i a (zz :: Effects).
CoPipe i o (Eff zz) a -> ScopedEff (Coroutine i o) zz a -> Eff zz a
withCoPipe ((i -> ScopedEff (Coroutine o i) zz a) -> CoPipe i o (Eff zz) a
forall o i a (zz :: Effects).
CoPipeSEff i o zz a -> CoPipe i o (Eff zz) a
toCoPipe i -> Handler (Coroutine o i) s -> Eff ('Union s zz) a
i -> ScopedEff (Coroutine o i) zz a
g) Handler (Coroutine i o) s -> Eff (s :& zz) a
ScopedEff (Coroutine i o) zz a
f

-- | Iterate through a coroutine:
-- execute the loop body @o -> Eff zz i@ for every call to 'Yield' in the coroutine.
forCoroutine :: forall o i a zz.
  ScopedEff (Coroutine o i) zz a ->  -- ^ Iterator
  (o -> Eff zz i) ->  -- ^ Loop body
  Eff zz a
forCoroutine :: forall o i a (zz :: Effects).
ScopedEff (Coroutine o i) zz a -> (o -> Eff zz i) -> Eff zz a
forCoroutine ScopedEff (Coroutine o i) zz a
f o -> Eff zz i
h = HandlerBody (Coroutine o i) zz a
-> ScopedEff (Coroutine o i) zz a -> Eff zz a
forall (f :: AEffect) (ss :: Effects) a.
HandlerBody f ss a -> ScopedEff f ss a -> Eff ss a
handle Coroutine o i x -> (x -> Eff zz a) -> Eff zz a
HandlerBody (Coroutine o i) zz a
coroutineHandler Handler (Coroutine o i) s -> Eff (s :& zz) a
ScopedEff (Coroutine o i) zz a
f
  where
    coroutineHandler :: HandlerBody (Coroutine o i) zz a
    coroutineHandler :: HandlerBody (Coroutine o i) zz a
coroutineHandler (Yield o
o) x -> Eff zz a
k = o -> Eff zz i
h o
o Eff zz i -> (i -> Eff zz a) -> Eff zz a
forall a b. Eff zz a -> (a -> Eff zz b) -> Eff zz b
forall (m :: AEffect) a b. Monad m => m a -> (a -> m b) -> m b
>>= i -> Eff zz a
x -> Eff zz a
k