{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE NoMonomorphismRestriction #-}

-- | Datatypes and definitions used by Churro library.
-- 
-- Expand instances for additional documentation!

module Control.Churro.Types where

import Prelude hiding (id, (.))
import Control.Arrow
import Control.Category
import Control.Concurrent.Async (cancel, wait, Async, async)
import Data.Void
import Control.Exception (finally)

-- $setup
-- 
-- We import the library for testing, although this would be a circular import in the module itself.
-- 
-- >>> import Control.Churro

-- ** Data, Classes and Instances

-- | The core datatype for the library.
-- 
-- Parameters `t`, `i` and `o` represent the transport, input, and output types respectively.
-- 
-- The items on transports are wrapped in `Maybe` to allow signalling of completion of a source.
-- 
-- When building a program by composing Churros, the output Transport of one
-- Churro is fed into the input Transports of other Churros.
-- 
-- Type families are used to allow the in/out channels to have different types
-- and prevent accidentally reading/writing from the wrong transport.
-- 
-- Convenience types of `Source`, `Sink`, and `DoubleDipped` are also defined,
-- although use is not required.
-- 
data Churro a t i o   = Churro { Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro :: IO (In t (Maybe i), Out t (Maybe o), Async a) }
type Source a t   o   = Churro a t Void o
type Sink   a t i     = Churro a t i Void
type DoubleDipped a t = Churro a t Void Void

-- | The transport method is abstracted via the Transport class
-- 
-- This allows use of pure or impure channels, such as:
-- 
-- * Chan (Included in `Control.Churro.Transport.Chan`)
-- * TChan
-- * Seq
-- * Unagi
-- * Various buffered options
-- 
-- Transports used in conjunction with Churros wrap items in Maybe so that once
-- a source has been depleted it can signal completion with a Nothing item.
-- 
-- The flex method returns two transports, so that channels such as unagi that
-- create an in/outs pair can have a Transport instance.
-- 
-- Channels like Chan that have a single channel act as in/out simply reuse the
-- same channel in the pair returned.
-- 
class Transport (t :: * -> *) where
    data In  t :: * -> *
    data Out t :: * -> *
    flex :: IO (In t a, Out t a)  -- ^ Create a new pair of transports.
    yank :: Out t a -> IO a       -- ^ Yank an item of the Transport
    yeet :: In t a -> a -> IO ()  -- ^ Yeet an item onto the Transport

-- | Covariant functor instance for Churro - Maps over the output.
-- 
-- >>> let s = sourceList [1,2]
-- >>> runWaitChan $ s >>> sinkPrint
-- 1
-- 2
-- 
-- >>> runWaitChan $ fmap succ s >>> sinkPrint
-- 2
-- 3
instance Transport t => Functor (Churro a t i) where
    fmap :: (a -> b) -> Churro a t i a -> Churro a t i b
fmap a -> b
f Churro a t i a
c = IO (In t (Maybe i), Out t (Maybe b), Async a) -> Churro a t i b
forall a (t :: * -> *) i o.
IO (In t (Maybe i), Out t (Maybe o), Async a) -> Churro a t i o
Churro do
        (In t (Maybe i)
i,Out t (Maybe a)
o,Async a
a) <- Churro a t i a -> IO (In t (Maybe i), Out t (Maybe a), Async a)
forall a (t :: * -> *) i o.
Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro Churro a t i a
c
        (In t (Maybe b)
i',Out t (Maybe b)
o') <- IO (In t (Maybe b), Out t (Maybe b))
forall (t :: * -> *) a. Transport t => IO (In t a, Out t a)
flex
        Async a
a' <- IO a -> IO (Async a)
forall a. IO a -> IO (Async a)
async do
            IO () -> IO a -> IO a
forall b a. IO b -> IO a -> IO a
finally' (Async a -> IO ()
forall a. Async a -> IO ()
cancel Async a
a) do
                (a -> b) -> Out t (Maybe a) -> In t (Maybe b) -> IO ()
forall (t :: * -> *) a b.
Transport t =>
(a -> b) -> Out t (Maybe a) -> In t (Maybe b) -> IO ()
c2c a -> b
f Out t (Maybe a)
o In t (Maybe b)
i'
                Async a -> IO a
forall a. Async a -> IO a
wait Async a
a
        (In t (Maybe i), Out t (Maybe b), Async a)
-> IO (In t (Maybe i), Out t (Maybe b), Async a)
forall (m :: * -> *) a. Monad m => a -> m a
return (In t (Maybe i)
i,Out t (Maybe b)
o',Async a
a')

-- | The Category instance allows for the creation of Churro pipelines.
-- 
-- All other examples of the form `a >>> b` use this instance.
-- 
-- The `id` method creates a passthrough arrow.
-- There isn't usually a reason to use `id` directly as it has no effect:
-- 
-- >>> runWaitChan $ pure 1 >>> id >>> id >>> id >>> sinkPrint
-- 1
instance (Transport t, Monoid a) => Category (Churro a t) where
    id :: Churro a t a a
id = IO (In t (Maybe a), Out t (Maybe a), Async a) -> Churro a t a a
forall a (t :: * -> *) i o.
IO (In t (Maybe i), Out t (Maybe o), Async a) -> Churro a t i o
Churro do
        (In t (Maybe a)
i,Out t (Maybe a)
o) <- IO (In t (Maybe a), Out t (Maybe a))
forall (t :: * -> *) a. Transport t => IO (In t a, Out t a)
flex
        Async a
a     <- IO a -> IO (Async a)
forall a. IO a -> IO (Async a)
async IO a
forall a. Monoid a => a
mempty
        (In t (Maybe a), Out t (Maybe a), Async a)
-> IO (In t (Maybe a), Out t (Maybe a), Async a)
forall (m :: * -> *) a. Monad m => a -> m a
return (In t (Maybe a)
i,Out t (Maybe a)
o,Async a
a)

    Churro a t b c
g . :: Churro a t b c -> Churro a t a b -> Churro a t a c
. Churro a t a b
f = Churro a t a b
f Churro a t a b -> Churro a t b c -> Churro a t a c
forall (t :: * -> *) fo gi a1 fi a2 go.
(Transport t, fo ~ gi) =>
Churro a1 t fi fo -> Churro a2 t gi go -> Churro a2 t fi go
>>>> Churro a t b c
g

-- | Category style composition that allows for return type to change downstream.
-- 
(>>>>) :: (Transport t, fo ~ gi) => Churro a1 t fi fo -> Churro a2 t gi go -> Churro a2 t fi go
Churro a1 t fi fo
f >>>> :: Churro a1 t fi fo -> Churro a2 t gi go -> Churro a2 t fi go
>>>> Churro a2 t gi go
g = IO (In t (Maybe fi), Out t (Maybe go), Async a2)
-> Churro a2 t fi go
forall a (t :: * -> *) i o.
IO (In t (Maybe i), Out t (Maybe o), Async a) -> Churro a t i o
Churro do
    (In t (Maybe fi)
fi, Out t (Maybe fo)
fo, Async a1
fa) <- Churro a1 t fi fo
-> IO (In t (Maybe fi), Out t (Maybe fo), Async a1)
forall a (t :: * -> *) i o.
Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro Churro a1 t fi fo
f
    (In t (Maybe gi)
gi, Out t (Maybe go)
go, Async a2
ga) <- Churro a2 t gi go
-> IO (In t (Maybe gi), Out t (Maybe go), Async a2)
forall a (t :: * -> *) i o.
Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro Churro a2 t gi go
g
    Async ()
a <- IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async do (fo -> gi) -> Out t (Maybe fo) -> In t (Maybe gi) -> IO ()
forall (t :: * -> *) a b.
Transport t =>
(a -> b) -> Out t (Maybe a) -> In t (Maybe b) -> IO ()
c2c fo -> gi
forall k (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id Out t (Maybe fo)
fo In t (Maybe gi)
gi
    Async a2
b <- IO a2 -> IO (Async a2)
forall a. IO a -> IO (Async a)
async do
        IO () -> IO a2 -> IO a2
forall b a. IO b -> IO a -> IO a
finally' (Async () -> IO ()
forall a. Async a -> IO ()
cancel Async ()
a IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Async a1 -> IO ()
forall a. Async a -> IO ()
cancel Async a1
fa IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Async a2 -> IO ()
forall a. Async a -> IO ()
cancel Async a2
ga) do
            a2
r <- Async a2 -> IO a2
forall a. Async a -> IO a
wait Async a2
ga
            Async a1 -> IO ()
forall a. Async a -> IO ()
cancel Async a1
fa
            Async () -> IO ()
forall a. Async a -> IO ()
cancel Async ()
a
            a2 -> IO a2
forall (m :: * -> *) a. Monad m => a -> m a
return a2
r
    (In t (Maybe fi), Out t (Maybe go), Async a2)
-> IO (In t (Maybe fi), Out t (Maybe go), Async a2)
forall (m :: * -> *) a. Monad m => a -> m a
return (In t (Maybe fi)
fi, Out t (Maybe go)
go, Async a2
b)

-- | The Applicative instance allows for pairwise composition of Churro pipelines.
--   Once again this is covariat and the composition occurs on the output transports of the Churros.
-- 
--  The `pure` method allows for the creation of a Churro yielding a single item.
-- 
-- TODO: Generalise () to a Monoid constraint.
-- 
instance Transport t => Applicative (Churro () t Void) where
    pure :: a -> Churro () t Void a
pure = a -> Churro () t Void a
forall (t :: * -> *) a o i.
(Transport t, Monoid a) =>
o -> Churro a t i o
pure'

    Churro () t Void (a -> b)
f <*> :: Churro () t Void (a -> b)
-> Churro () t Void a -> Churro () t Void b
<*> Churro () t Void a
g = (Out t (Maybe Void) -> In t (Maybe b) -> IO ())
-> Churro () t Void b
forall (t :: * -> *) i o a.
Transport t =>
(Out t (Maybe i) -> In t (Maybe o) -> IO a) -> Churro a t i o
buildChurro \Out t (Maybe Void)
_i In t (Maybe b)
o -> do
        (In t (Maybe Void)
_fi, Out t (Maybe (a -> b))
fo, Async ()
fa) <- Churro () t Void (a -> b)
-> IO (In t (Maybe Void), Out t (Maybe (a -> b)), Async ())
forall a (t :: * -> *) i o.
Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro Churro () t Void (a -> b)
f
        (In t (Maybe Void)
_gi, Out t (Maybe a)
go, Async ()
ga) <- Churro () t Void a
-> IO (In t (Maybe Void), Out t (Maybe a), Async ())
forall a (t :: * -> *) i o.
Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro Churro () t Void a
g

        let
            prog :: IO ()
            prog :: IO ()
prog = do
                Maybe (a -> b)
fx <- Out t (Maybe (a -> b)) -> IO (Maybe (a -> b))
forall (t :: * -> *) a. Transport t => Out t a -> IO a
yank Out t (Maybe (a -> b))
fo
                Maybe a
gx <- Out t (Maybe a) -> IO (Maybe a)
forall (t :: * -> *) a. Transport t => Out t a -> IO a
yank Out t (Maybe a)
go
                case (Maybe (a -> b)
fx, Maybe a
gx) of
                    (Just a -> b
f', Just a
g') -> (In t (Maybe b) -> Maybe b -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe b)
o (Maybe b -> IO ()) -> Maybe b -> IO ()
forall a b. (a -> b) -> a -> b
$ b -> Maybe b
forall a. a -> Maybe a
Just (a -> b
f' a
g')) IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO ()
prog
                    (Maybe (a -> b), Maybe a)
_                  -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

        -- TODO: Should we cancel asyncs here in finally block?
        IO ()
prog
        In t (Maybe b) -> Maybe b -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe b)
o Maybe b
forall a. Maybe a
Nothing
        Async () -> IO ()
forall a. Async a -> IO a
wait Async ()
fa
        Async () -> IO ()
forall a. Async a -> IO a
wait Async ()
ga

-- | More general variant of `pure` with Monoid constraint.
pure' :: (Transport t, Monoid a) => o -> Churro a t i o
pure' :: o -> Churro a t i o
pure' o
x = (Out t (Maybe i) -> In t (Maybe o) -> IO a) -> Churro a t i o
forall (t :: * -> *) i o a.
Transport t =>
(Out t (Maybe i) -> In t (Maybe o) -> IO a) -> Churro a t i o
buildChurro \Out t (Maybe i)
_i In t (Maybe o)
o -> In t (Maybe o) -> Maybe o -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe o)
o (o -> Maybe o
forall a. a -> Maybe a
Just o
x) IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> In t (Maybe o) -> Maybe o -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe o)
o Maybe o
forall a. Maybe a
Nothing IO () -> IO a -> IO a
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
forall a. Monoid a => a
mempty

-- | The Arrow instance allows for building non-cyclic directed graphs of churros.
-- 
--  The `arr` method allows for the creation of a that maps items with a pure function.
--  This is equivalent to `fmap f id`. This is more general and exposed via arr`.
-- 
-- >>> :set -XArrows
-- >>> :{
-- let sect  = process $ \x@(_x,_y,z) -> print x >> return z
--     graph =
--       proc i -> do
--         j <- arr succ  -< i
--         k <- arr show  -< j
--         l <- arr succ  -< j
--         m <- arr (> 5) -< j
--         n <- sect      -< (k,l,m)
--         o <- arr not   -< n
--         p <- delay 0.1 -< o
--         sinkPrint      -< p
-- in
-- runWaitChan $ sourceList [1,5,30] >>> graph
-- :}
-- ("2",3,False)
-- ("6",7,True)
-- ("31",32,True)
-- True
-- False
-- False
-- 
-- The other Arrow methods are also usable:
-- 
-- >>> runWaitChan $ pure 1 >>> (arr show &&& arr succ) >>> sinkPrint
-- ("1",2)
instance Transport t => Arrow (Churro () t) where
    arr :: (b -> c) -> Churro () t b c
arr = (b -> c) -> Churro () t b c
forall (cat :: * -> * -> *) a b.
(Functor (cat a), Category cat) =>
(a -> b) -> cat a b
arr'

    first :: Churro () t b c -> Churro () t (b, d) (c, d)
first Churro () t b c
c = IO (In t (Maybe (b, d)), Out t (Maybe (c, d)), Async ())
-> Churro () t (b, d) (c, d)
forall a (t :: * -> *) i o.
IO (In t (Maybe i), Out t (Maybe o), Async a) -> Churro a t i o
Churro do
        (In t (Maybe b)
i,Out t (Maybe c)
o,Async ()
a)   <- Churro () t b c -> IO (In t (Maybe b), Out t (Maybe c), Async ())
forall a (t :: * -> *) i o.
Churro a t i o -> IO (In t (Maybe i), Out t (Maybe o), Async a)
runChurro Churro () t b c
c
        (In t (Maybe (b, d))
ai',Out t (Maybe (b, d))
ao') <- IO (In t (Maybe (b, d)), Out t (Maybe (b, d)))
forall (t :: * -> *) a. Transport t => IO (In t a, Out t a)
flex
        (In t (Maybe (c, d))
bi',Out t (Maybe (c, d))
bo') <- IO (In t (Maybe (c, d)), Out t (Maybe (c, d)))
forall (t :: * -> *) a. Transport t => IO (In t a, Out t a)
flex

        let go :: IO ()
go = do
                Maybe (b, d)
is <- Out t (Maybe (b, d)) -> IO (Maybe (b, d))
forall (t :: * -> *) a. Transport t => Out t a -> IO a
yank Out t (Maybe (b, d))
ao'
                In t (Maybe b) -> Maybe b -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe b)
i (((b, d) -> b) -> Maybe (b, d) -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (b, d) -> b
forall a b. (a, b) -> a
fst Maybe (b, d)
is)

                Maybe c
os <- Out t (Maybe c) -> IO (Maybe c)
forall (t :: * -> *) a. Transport t => Out t a -> IO a
yank Out t (Maybe c)
o
                In t (Maybe (c, d)) -> Maybe (c, d) -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe (c, d))
bi' (Maybe (c, d) -> IO ()) -> Maybe (c, d) -> IO ()
forall a b. (a -> b) -> a -> b
$ (,) (c -> d -> (c, d)) -> Maybe c -> Maybe (d -> (c, d))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe c
os Maybe (d -> (c, d)) -> Maybe d -> Maybe (c, d)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ((b, d) -> d) -> Maybe (b, d) -> Maybe d
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (b, d) -> d
forall a b. (a, b) -> b
snd Maybe (b, d)
is

                case (Maybe (b, d)
is, Maybe c
os) of
                    (Just (b, d)
_, Just c
_) -> IO ()
go
                    (Maybe (b, d), Maybe c)
_ -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

        Async ()
a' <- IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async do
            IO ()
go
            In t (Maybe (c, d)) -> Maybe (c, d) -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe (c, d))
bi' Maybe (c, d)
forall a. Maybe a
Nothing
            Async () -> IO ()
forall a. Async a -> IO a
wait Async ()
a

        (In t (Maybe (b, d)), Out t (Maybe (c, d)), Async ())
-> IO (In t (Maybe (b, d)), Out t (Maybe (c, d)), Async ())
forall (m :: * -> *) a. Monad m => a -> m a
return (In t (Maybe (b, d))
ai',Out t (Maybe (c, d))
bo',Async ()
a')

-- | More general version of `arr`.
-- 
-- Useful when building pipelines that need to work with return types.
arr' :: (Functor (cat a), Category cat) => (a -> b) -> cat a b
arr' :: (a -> b) -> cat a b
arr' a -> b
f = (a -> b) -> cat a a -> cat a b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f cat a a
forall k (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id

-- ** Helpers

-- | A helper to facilitate constructing a Churro that makes new input and output transports available for manipulation.
-- 
-- The manipulations performed are carried out in the async action associated with the Churro
-- 
buildChurro :: Transport t => (Out t (Maybe i) -> In t (Maybe o) -> IO a) -> Churro a t i o
buildChurro :: (Out t (Maybe i) -> In t (Maybe o) -> IO a) -> Churro a t i o
buildChurro Out t (Maybe i) -> In t (Maybe o) -> IO a
cb = (In t (Maybe i) -> Out t (Maybe i) -> In t (Maybe o) -> IO a)
-> Churro a t i o
forall (t :: * -> *) i o a.
Transport t =>
(In t (Maybe i) -> Out t (Maybe i) -> In t (Maybe o) -> IO a)
-> Churro a t i o
buildChurro' \In t (Maybe i)
_o' Out t (Maybe i)
i In t (Maybe o)
o -> Out t (Maybe i) -> In t (Maybe o) -> IO a
cb Out t (Maybe i)
i In t (Maybe o)
o

-- | A version of `buildChurro` that also passes the original input to the callback so that you can reschedule items.
-- 
-- Used by "retry" style functions.
-- 
buildChurro' :: Transport t => (In t (Maybe i) -> Out t (Maybe i) -> In t (Maybe o) -> IO a) -> Churro a t i o
buildChurro' :: (In t (Maybe i) -> Out t (Maybe i) -> In t (Maybe o) -> IO a)
-> Churro a t i o
buildChurro' In t (Maybe i) -> Out t (Maybe i) -> In t (Maybe o) -> IO a
cb = IO (In t (Maybe i), Out t (Maybe o), Async a) -> Churro a t i o
forall a (t :: * -> *) i o.
IO (In t (Maybe i), Out t (Maybe o), Async a) -> Churro a t i o
Churro do
    (In t (Maybe i)
ai,Out t (Maybe i)
ao) <- IO (In t (Maybe i), Out t (Maybe i))
forall (t :: * -> *) a. Transport t => IO (In t a, Out t a)
flex
    (In t (Maybe o)
bi,Out t (Maybe o)
bo) <- IO (In t (Maybe o), Out t (Maybe o))
forall (t :: * -> *) a. Transport t => IO (In t a, Out t a)
flex
    Async a
a       <- IO a -> IO (Async a)
forall a. IO a -> IO (Async a)
async do In t (Maybe i) -> Out t (Maybe i) -> In t (Maybe o) -> IO a
cb In t (Maybe i)
ai Out t (Maybe i)
ao In t (Maybe o)
bi
    (In t (Maybe i), Out t (Maybe o), Async a)
-> IO (In t (Maybe i), Out t (Maybe o), Async a)
forall (m :: * -> *) a. Monad m => a -> m a
return (In t (Maybe i)
ai,Out t (Maybe o)
bo,Async a
a)

-- | Yeet all items from a list into a transport.
-- 
yeetList :: (Foldable f, Transport t) => In t a -> f a -> IO ()
yeetList :: In t a -> f a -> IO ()
yeetList In t a
t = (a -> IO ()) -> f a -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (In t a -> a -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t a
t)

-- | Yank all items from a Raw transport into a list.
-- 
--   Won't terminate until the transport has been consumed.
-- 
yankList :: Transport t => Out t (Maybe a) -> IO [a]
yankList :: Out t (Maybe a) -> IO [a]
yankList = (Out t (Maybe a) -> (a -> IO [a]) -> IO [a])
-> (a -> IO [a]) -> Out t (Maybe a) -> IO [a]
forall a b c. (a -> b -> c) -> b -> a -> c
flip Out t (Maybe a) -> (a -> IO [a]) -> IO [a]
forall (t :: * -> *) a i.
(Transport t, Monoid a) =>
Out t (Maybe i) -> (i -> IO a) -> IO a
yankAll ([a] -> IO [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([a] -> IO [a]) -> (a -> [a]) -> a -> IO [a]
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure)

-- | Yank each item from a transport into a callback.
-- 
yankAll :: (Transport t, Monoid a) => Out t (Maybe i) -> (i -> IO a) -> IO a
yankAll :: Out t (Maybe i) -> (i -> IO a) -> IO a
yankAll Out t (Maybe i)
c i -> IO a
f = do
    Maybe i
x <- Out t (Maybe i) -> IO (Maybe i)
forall (t :: * -> *) a. Transport t => Out t a -> IO a
yank Out t (Maybe i)
c
    case Maybe i
x of
        Maybe i
Nothing -> IO a
forall a. Monoid a => a
mempty
        Just i
y  -> i -> IO a
f i
y IO a -> IO a -> IO a
forall a. Semigroup a => a -> a -> a
<> Out t (Maybe i) -> (i -> IO a) -> IO a
forall (t :: * -> *) a i.
(Transport t, Monoid a) =>
Out t (Maybe i) -> (i -> IO a) -> IO a
yankAll Out t (Maybe i)
c i -> IO a
f

-- | Yank each raw item from a transport into a callback.
-- 
-- The items are wrapped in Maybes and when all items are yanked, Nothing is fed to the callback.
-- 
yankAll' :: (Transport t, Monoid b) => Out t (Maybe a) -> (Maybe a -> IO b) -> IO b
yankAll' :: Out t (Maybe a) -> (Maybe a -> IO b) -> IO b
yankAll' Out t (Maybe a)
c Maybe a -> IO b
f = do
    b
x <- Out t (Maybe a) -> (a -> IO b) -> IO b
forall (t :: * -> *) a i.
(Transport t, Monoid a) =>
Out t (Maybe i) -> (i -> IO a) -> IO a
yankAll Out t (Maybe a)
c (Maybe a -> IO b
f (Maybe a -> IO b) -> (a -> Maybe a) -> a -> IO b
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. a -> Maybe a
forall a. a -> Maybe a
Just)
    b
y <- Maybe a -> IO b
f Maybe a
forall a. Maybe a
Nothing
    b -> IO b
forall (m :: * -> *) a. Monad m => a -> m a
return (b
x b -> b -> b
forall a. Semigroup a => a -> a -> a
<> b
y)

-- | Yank then Yeet each item from one Transport into another.
-- 
-- Raw items are used so `Nothing` should be Yeeted once the transport is depleted.
-- 
c2c :: Transport t => (a -> b) -> Out t (Maybe a) -> In t (Maybe b) -> IO ()
c2c :: (a -> b) -> Out t (Maybe a) -> In t (Maybe b) -> IO ()
c2c a -> b
f Out t (Maybe a)
o In t (Maybe b)
i = Out t (Maybe a) -> (Maybe a -> IO ()) -> IO ()
forall (t :: * -> *) b a.
(Transport t, Monoid b) =>
Out t (Maybe a) -> (Maybe a -> IO b) -> IO b
yankAll' Out t (Maybe a)
o (In t (Maybe b) -> Maybe b -> IO ()
forall (t :: * -> *) a. Transport t => In t a -> a -> IO ()
yeet In t (Maybe b)
i (Maybe b -> IO ()) -> (Maybe a -> Maybe b) -> Maybe a -> IO ()
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f)

-- | Flipped `finally`.
finally' :: IO b -> IO a -> IO a
finally' :: IO b -> IO a -> IO a
finally' = (IO a -> IO b -> IO a) -> IO b -> IO a -> IO a
forall a b c. (a -> b -> c) -> b -> a -> c
flip IO a -> IO b -> IO a
forall a b. IO a -> IO b -> IO a
finally