summaryrefslogtreecommitdiff
path: root/testsuite/tests/indexed-types/should_compile/T3787.hs
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/tests/indexed-types/should_compile/T3787.hs')
-rw-r--r--testsuite/tests/indexed-types/should_compile/T3787.hs475
1 files changed, 475 insertions, 0 deletions
diff --git a/testsuite/tests/indexed-types/should_compile/T3787.hs b/testsuite/tests/indexed-types/should_compile/T3787.hs
new file mode 100644
index 0000000000..955b6a1cdd
--- /dev/null
+++ b/testsuite/tests/indexed-types/should_compile/T3787.hs
@@ -0,0 +1,475 @@
+{-
+ Copyright 2009 Mario Blazevic
+
+ This file is part of the Streaming Component Combinators (SCC) project.
+
+ The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
+ version.
+
+ SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with SCC. If not, see
+ <http://www.gnu.org/licenses/>.
+-}
+
+-- | Module "Trampoline" defines the trampoline computations and their basic building blocks.
+
+{-# LANGUAGE ScopedTypeVariables, Rank2Types, MultiParamTypeClasses, TypeFamilies, KindSignatures,
+ FlexibleContexts, FlexibleInstances, OverlappingInstances, UndecidableInstances
+ #-}
+
+module T3787 where
+
+import Control.Concurrent (forkIO)
+import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
+import Control.Monad (liftM, liftM2, when)
+import Control.Monad.Identity
+import Control.Monad.Trans (MonadTrans(..))
+
+import Data.Foldable (toList)
+import Data.Maybe (maybe)
+import Data.Sequence (Seq, viewl)
+
+par, pseq :: a -> b -> b
+par = error "urk"
+pseq = error "urk"
+
+-- | Class of monads that can perform two computations in parallel.
+class Monad m => ParallelizableMonad m where
+ -- | Combine two computations into a single parallel computation. Default implementation of `parallelize` is
+ -- @liftM2 (,)@
+ parallelize :: m a -> m b -> m (a, b)
+ parallelize = liftM2 (,)
+
+-- | Any monad that allows the result value to be extracted, such as `Identity` or `Maybe` monad, can implement
+-- `parallelize` by using `par`.
+instance ParallelizableMonad Identity where
+ parallelize ma mb = let a = runIdentity ma
+ b = runIdentity mb
+ in a `par` (b `pseq` a `pseq` Identity (a, b))
+
+instance ParallelizableMonad Maybe where
+ parallelize ma mb = case ma `par` (mb `pseq` (ma, mb))
+ of (Just a, Just b) -> Just (a, b)
+ _ -> Nothing
+
+-- | IO is parallelizable by `forkIO`.
+instance ParallelizableMonad IO where
+ parallelize ma mb = do va <- newEmptyMVar
+ vb <- newEmptyMVar
+ forkIO (ma >>= putMVar va)
+ forkIO (mb >>= putMVar vb)
+ a <- takeMVar va
+ b <- takeMVar vb
+ return (a, b)
+
+-- | Suspending monadic computations.
+newtype Trampoline s m r = Trampoline {
+ -- | Run the next step of a `Trampoline` computation.
+ bounce :: m (TrampolineState s m r)
+ }
+
+data TrampolineState s m r =
+ -- | Trampoline computation is finished with final value /r/.
+ Done r
+ -- | Computation is suspended, its remainder is embedded in the functor /s/.
+ | Suspend! (s (Trampoline s m r))
+
+instance (Functor s, Monad m) => Monad (Trampoline s m) where
+ return x = Trampoline (return (Done x))
+ t >>= f = Trampoline (bounce t >>= apply f)
+ where apply f (Done x) = bounce (f x)
+ apply f (Suspend s) = return (Suspend (fmap (>>= f) s))
+
+instance (Functor s, ParallelizableMonad m) => ParallelizableMonad (Trampoline s m) where
+ parallelize t1 t2 = Trampoline $ liftM combine $ parallelize (bounce t1) (bounce t2) where
+ combine (Done x, Done y) = Done (x, y)
+ combine (Suspend s, Done y) = Suspend (fmap (liftM $ \x-> (x, y)) s)
+ combine (Done x, Suspend s) = Suspend (fmap (liftM $ (,) x) s)
+ combine (Suspend s1, Suspend s2) = Suspend (fmap (parallelize $ suspend s1) s2)
+
+instance Functor s => MonadTrans (Trampoline s) where
+ lift = Trampoline . liftM Done
+
+data Yield x y = Yield! x y
+instance Functor (Yield x) where
+ fmap f (Yield x y) = Yield x (f y)
+
+data Await x y = Await! (x -> y)
+instance Functor (Await x) where
+ fmap f (Await g) = Await (f . g)
+
+data EitherFunctor l r x = LeftF (l x) | RightF (r x)
+instance (Functor l, Functor r) => Functor (EitherFunctor l r) where
+ fmap f (LeftF l) = LeftF (fmap f l)
+ fmap f (RightF r) = RightF (fmap f r)
+
+newtype NestedFunctor l r x = NestedFunctor (l (r x))
+instance (Functor l, Functor r) => Functor (NestedFunctor l r) where
+ fmap f (NestedFunctor lr) = NestedFunctor ((fmap . fmap) f lr)
+
+data SomeFunctor l r x = LeftSome (l x) | RightSome (r x) | Both (NestedFunctor l r x)
+instance (Functor l, Functor r) => Functor (SomeFunctor l r) where
+ fmap f (LeftSome l) = LeftSome (fmap f l)
+ fmap f (RightSome r) = RightSome (fmap f r)
+ fmap f (Both lr) = Both (fmap f lr)
+
+type TryYield x = EitherFunctor (Yield x) (Await Bool)
+
+suspend :: (Monad m, Functor s) => s (Trampoline s m x) -> Trampoline s m x
+suspend s = Trampoline (return (Suspend s))
+
+yield :: forall m x. Monad m => x -> Trampoline (Yield x) m ()
+yield x = suspend (Yield x (return ()))
+
+await :: forall m x. Monad m => Trampoline (Await x) m x
+await = suspend (Await return)
+
+tryYield :: forall m x. Monad m => x -> Trampoline (TryYield x) m Bool
+tryYield x = suspend (LeftF (Yield x (suspend (RightF (Await return)))))
+
+canYield :: forall m x. Monad m => Trampoline (TryYield x) m Bool
+canYield = suspend (RightF (Await return))
+
+fromTrampoline :: Monad m => Trampoline s m x -> m x
+fromTrampoline t = bounce t >>= \(Done x)-> return x
+
+runTrampoline :: Monad m => Trampoline Identity m x -> m x
+runTrampoline = fromTrampoline
+
+pogoStick :: (Functor s, Monad m) => (s (Trampoline s m x) -> Trampoline s m x) -> Trampoline s m x -> m x
+pogoStick reveal t = bounce t
+ >>= \s-> case s
+ of Done result -> return result
+ Suspend c -> pogoStick reveal (reveal c)
+
+pogoStickNested :: (Functor s1, Functor s2, Monad m) =>
+ (s2 (Trampoline (EitherFunctor s1 s2) m x) -> Trampoline (EitherFunctor s1 s2) m x)
+ -> Trampoline (EitherFunctor s1 s2) m x -> Trampoline s1 m x
+pogoStickNested reveal t =
+ Trampoline{bounce= bounce t
+ >>= \s-> case s
+ of Done result -> return (Done result)
+ Suspend (LeftF s) -> return (Suspend (fmap (pogoStickNested reveal) s))
+ Suspend (RightF c) -> bounce (pogoStickNested reveal (reveal c))
+ }
+
+nest :: (Functor a, Functor b) => a x -> b y -> NestedFunctor a b (x, y)
+nest a b = NestedFunctor $ fmap (\x-> fmap ((,) x) b) a
+
+-- couple :: (Monad m, Functor s1, Functor s2) =>
+-- Trampoline s1 m x -> Trampoline s2 m y -> Trampoline (NestedFunctor s1 s2) m (x, y)
+-- couple t1 t2 = Trampoline{bounce= do ts1 <- bounce t1
+-- ts2 <- bounce t2
+-- case (ts1, ts2) of (Done x, Done y) -> return $ Done (x, y)
+-- (Suspend s1, Suspend s2) -> return $ Suspend $
+-- fmap (uncurry couple) (nest s1 s2)
+-- }
+
+coupleAlternating :: (Monad m, Functor s1, Functor s2) =>
+ Trampoline s1 m x -> Trampoline s2 m y -> Trampoline (SomeFunctor s1 s2) m (x, y)
+coupleAlternating t1 t2 =
+ Trampoline{bounce= do ts1 <- bounce t1
+ ts2 <- bounce t2
+ case (ts1, ts2) of (Done x, Done y) -> return $ Done (x, y)
+ (Suspend s1, Suspend s2) ->
+ return $ Suspend $ fmap (uncurry coupleAlternating) (Both $ nest s1 s2)
+ (Done x, Suspend s2) ->
+ return $ Suspend $ fmap (coupleAlternating (return x)) (RightSome s2)
+ (Suspend s1, Done y) ->
+ return $ Suspend $ fmap (flip coupleAlternating (return y)) (LeftSome s1)
+ }
+
+coupleParallel :: (ParallelizableMonad m, Functor s1, Functor s2) =>
+ Trampoline s1 m x -> Trampoline s2 m y -> Trampoline (SomeFunctor s1 s2) m (x, y)
+coupleParallel t1 t2 =
+ Trampoline{bounce= parallelize (bounce t1) (bounce t2)
+ >>= \pair-> case pair
+ of (Done x, Done y) -> return $ Done (x, y)
+ (Suspend s1, Suspend s2) ->
+ return $ Suspend $ fmap (uncurry coupleParallel) (Both $ nest s1 s2)
+ (Done x, Suspend s2) ->
+ return $ Suspend $ fmap (coupleParallel (return x)) (RightSome s2)
+ (Suspend s1, Done y) ->
+ return $ Suspend $ fmap (flip coupleParallel (return y)) (LeftSome s1)
+ }
+
+coupleNested :: (Monad m, Functor s0, Functor s1, Functor s2) =>
+ Trampoline (EitherFunctor s0 s1) m x -> Trampoline (EitherFunctor s0 s2) m y ->
+ Trampoline (EitherFunctor s0 (SomeFunctor s1 s2)) m (x, y)
+coupleNested t1 t2 =
+ Trampoline{bounce= do ts1 <- bounce t1
+ ts2 <- bounce t2
+ case (ts1, ts2) of (Done x, Done y) -> return $ Done (x, y)
+ (Suspend (RightF s), Done y) ->
+ return $ Suspend $ RightF $ fmap (flip coupleNested (return y)) (LeftSome s)
+ (Done x, Suspend (RightF s)) ->
+ return $ Suspend $ RightF $ fmap (coupleNested (return x)) (RightSome s)
+ (Suspend (RightF s1), Suspend (RightF s2)) ->
+ return $ Suspend $ RightF $ fmap (uncurry coupleNested) (Both $ nest s1 s2)
+ (Suspend (LeftF s), Done y) ->
+ return $ Suspend $ LeftF $ fmap (flip coupleNested (return y)) s
+ (Done x, Suspend (LeftF s)) ->
+ return $ Suspend $ LeftF $ fmap (coupleNested (return x)) s
+ (Suspend (LeftF s1), Suspend (LeftF s2)) ->
+ return $ Suspend $ LeftF $ fmap (coupleNested $ suspend $ LeftF s1) s2
+ }
+
+seesaw :: (Monad m, Functor s1, Functor s2) =>
+ (forall x y s t. (s ~ SomeFunctor s1 s2, t ~ Trampoline s m (x, y)) => s t -> t)
+ -> Trampoline s1 m x -> Trampoline s2 m y -> m (x, y)
+seesaw resolve t1 t2 = pogoStick resolve (coupleAlternating t1 t2)
+
+seesawParallel :: (ParallelizableMonad m, Functor s1, Functor s2) =>
+ (forall x y s t. (s ~ SomeFunctor s1 s2, t ~ Trampoline s m (x, y)) => s t -> t)
+ -> Trampoline s1 m x -> Trampoline s2 m y -> m (x, y)
+seesawParallel resolve t1 t2 = pogoStick resolve (coupleParallel t1 t2)
+
+resolveProducerConsumer :: forall a s s0 t t' m x.
+ (Functor s0, Monad m, s ~ SomeFunctor (TryYield a) (Await (Maybe a)),
+ t ~ Trampoline (EitherFunctor s0 s) m x) =>
+ s t -> t
+-- Arg :: s t
+-- (LeftSome (LeftF ...)) : SomeFunctor (EitherFunctor .. ..) (...) t
+resolveProducerConsumer (LeftSome (LeftF (Yield _ c))) = c
+resolveProducerConsumer (LeftSome (RightF (Await c))) = c False
+resolveProducerConsumer (RightSome (Await c)) = c Nothing
+resolveProducerConsumer (Both (NestedFunctor (LeftF (Yield x (Await c))))) = c (Just x)
+resolveProducerConsumer (Both (NestedFunctor (RightF (Await c)))) = suspend (RightF $ RightSome $ c True)
+
+couplePC :: ParallelizableMonad m => Trampoline (Yield a) m x -> Trampoline (Await (Maybe a)) m y -> m (x, y)
+couplePC t1 t2 = parallelize (bounce t1) (bounce t2)
+ >>= \(s1, s2)-> case (s1, s2)
+ of (Done x, Done y) -> return (x, y)
+ (Suspend (Yield x c1), Suspend (Await c2)) -> couplePC c1 (c2 $ Just x)
+ (Suspend (Yield _ c1), Done y) -> couplePC c1 (return y)
+ (Done x, Suspend (Await c2)) -> couplePC (return x) (c2 Nothing)
+
+coupleFinite :: ParallelizableMonad m => Trampoline (TryYield a) m x -> Trampoline (Await (Maybe a)) m y -> m (x, y)
+coupleFinite t1 t2 =
+ parallelize (bounce t1) (bounce t2)
+ >>= \(s1, s2)-> case (s1, s2)
+ of (Done x, Done y) -> return (x, y)
+ (Done x, Suspend (Await c2)) -> coupleFinite (return x) (c2 Nothing)
+ (Suspend (LeftF (Yield x c1)), Suspend (Await c2)) -> coupleFinite c1 (c2 $ Just x)
+ (Suspend (LeftF (Yield _ c1)), Done y) -> coupleFinite c1 (return y)
+ (Suspend (RightF (Await c1)), Suspend s2@Await{}) -> coupleFinite (c1 True) (suspend s2)
+ (Suspend (RightF (Await c1)), Done y) -> coupleFinite (c1 False) (return y)
+
+coupleFiniteSequential :: Monad m => Trampoline (TryYield a) m x -> Trampoline (Await (Maybe a)) m y -> m (x, y)
+coupleFiniteSequential t1 t2 =
+ bounce t1
+ >>= \s1-> bounce t2
+ >>= \s2-> case (s1, s2)
+ of (Done x, Done y) -> return (x, y)
+ (Done x, Suspend (Await c2)) -> coupleFiniteSequential (return x) (c2 Nothing)
+ (Suspend (LeftF (Yield x c1)), Suspend (Await c2)) -> coupleFiniteSequential c1 (c2 $ Just x)
+ (Suspend (LeftF (Yield _ c1)), Done y) -> coupleFiniteSequential c1 (return y)
+ (Suspend (RightF (Await c1)), Suspend s2@Await{}) -> coupleFiniteSequential (c1 True) (suspend s2)
+ (Suspend (RightF (Await c1)), Done y) -> coupleFiniteSequential (c1 False) (return y)
+
+-- coupleNested :: (Functor s, Monad m) =>
+-- Trampoline (EitherFunctor s (Yield a)) m x
+-- -> Trampoline (EitherFunctor s (Await (Maybe a))) m y -> Trampoline s m (x, y)
+
+-- coupleNested t1 t2 =
+-- lift (liftM2 (,) (bounce t1) (bounce t2))
+-- >>= \(s1, s2)-> case (s1, s2)
+-- of (Done x, Done y) -> return (x, y)
+-- (Suspend (RightF (Yield _ c1)), Done y) -> coupleNested c1 (return y)
+-- (Done x, Suspend (RightF (Await c2))) -> coupleNested (return x) (c2 Nothing)
+-- (Suspend (RightF (Yield x c1)), Suspend (RightF (Await c2))) -> coupleNested c1 (c2 $ Just x)
+-- (Suspend (LeftF s), Done y) -> suspend (fmap (flip coupleNested (return y)) s)
+-- (Done x, Suspend (LeftF s)) -> suspend (fmap (coupleNested (return x)) s)
+-- (Suspend (LeftF s1), Suspend (LeftF s2)) -> suspend (fmap (coupleNested $ suspend $ LeftF s1) s2)
+
+coupleNestedFinite :: (Functor s, ParallelizableMonad m) =>
+ Trampoline (SinkFunctor s a) m x -> Trampoline (SourceFunctor s a) m y -> Trampoline s m (x, y)
+coupleNestedFinite t1 t2 = lift (parallelize (bounce t1) (bounce t2))
+ >>= stepCouple coupleNestedFinite
+
+coupleNestedFiniteSequential :: (Functor s, Monad m) =>
+ Trampoline (SinkFunctor s a) m x
+ -> Trampoline (SourceFunctor s a) m y
+ -> Trampoline s m (x, y)
+coupleNestedFiniteSequential producer consumer =
+ pogoStickNested resolveProducerConsumer (coupleNested producer consumer)
+-- coupleNestedFiniteSequential t1 t2 = lift (liftM2 (,) (bounce t1) (bounce t2))
+-- >>= stepCouple coupleNestedFiniteSequential
+
+stepCouple :: (Functor s, Monad m) =>
+ (Trampoline (EitherFunctor s (TryYield a)) m x
+ -> Trampoline (EitherFunctor s (Await (Maybe a))) m y
+ -> Trampoline s m (x, y))
+ -> (TrampolineState (EitherFunctor s (TryYield a)) m x,
+ TrampolineState (EitherFunctor s (Await (Maybe a))) m y)
+ -> Trampoline s m (x, y)
+stepCouple f couple = case couple
+ of (Done x, Done y) -> return (x, y)
+ (Done x, Suspend (RightF (Await c2))) -> f (return x) (c2 Nothing)
+ (Suspend (RightF (LeftF (Yield _ c1))), Done y) -> f c1 (return y)
+ (Suspend (RightF (LeftF (Yield x c1))), Suspend (RightF (Await c2))) -> f c1 (c2 $ Just x)
+ (Suspend (RightF (RightF (Await c1))), Suspend s2@(RightF Await{})) -> f (c1 True) (suspend s2)
+ (Suspend (RightF (RightF (Await c1))), Done y) -> f (c1 False) (return y)
+ (Suspend (LeftF s), Done y) -> suspend (fmap (flip f (return y)) s)
+ (Done x, Suspend (LeftF s)) -> suspend (fmap (f (return x)) s)
+ (Suspend (LeftF s1), Suspend (LeftF s2)) -> suspend (fmap (f $ suspend $ LeftF s1) s2)
+ (Suspend (LeftF s1), Suspend (RightF s2)) -> suspend (fmap (flip f (suspend $ RightF s2)) s1)
+ (Suspend (RightF s1), Suspend (LeftF s2)) -> suspend (fmap (f (suspend $ RightF s1)) s2)
+
+local :: forall m l r x. (Functor r, Monad m) => Trampoline r m x -> Trampoline (EitherFunctor l r) m x
+local (Trampoline mr) = Trampoline (liftM inject mr)
+ where inject :: TrampolineState r m x -> TrampolineState (EitherFunctor l r) m x
+ inject (Done x) = Done x
+ inject (Suspend r) = Suspend (RightF $ fmap local r)
+
+out :: forall m l r x. (Functor l, Monad m) => Trampoline l m x -> Trampoline (EitherFunctor l r) m x
+out (Trampoline ml) = Trampoline (liftM inject ml)
+ where inject :: TrampolineState l m x -> TrampolineState (EitherFunctor l r) m x
+ inject (Done x) = Done x
+ inject (Suspend l) = Suspend (LeftF $ fmap out l)
+
+-- | Class of functors that can be lifted.
+class (Functor a, Functor d) => AncestorFunctor a d where
+ -- | Convert the ancestor functor into its descendant. The descendant functor typically contains the ancestor.
+ liftFunctor :: a x -> d x
+
+instance Functor a => AncestorFunctor a a where
+ liftFunctor = id
+instance (Functor a, Functor d', Functor d, d ~ EitherFunctor d' s, AncestorFunctor a d') => AncestorFunctor a d where
+ liftFunctor = LeftF . (liftFunctor :: a x -> d' x)
+
+liftOut :: forall m a d x. (Monad m, Functor a, AncestorFunctor a d) => Trampoline a m x -> Trampoline d m x
+liftOut (Trampoline ma) = Trampoline (liftM inject ma)
+ where inject :: TrampolineState a m x -> TrampolineState d m x
+ inject (Done x) = Done x
+ inject (Suspend a) = Suspend (liftFunctor $ fmap liftOut a)
+
+type SourceFunctor a x = EitherFunctor a (Await (Maybe x))
+type SinkFunctor a x = EitherFunctor a (TryYield x)
+
+-- | A 'Sink' can be used to yield values from any nested `Trampoline` computation whose functor provably descends from
+-- the functor /a/. It's the write-only end of a 'Pipe' communication channel.
+data Sink (m :: * -> *) a x =
+ Sink
+ {
+ -- | Function 'put' tries to put a value into the given `Sink`. The intervening 'Trampoline' computations suspend up
+ -- to the 'pipe' invocation that has created the argument sink. The result of 'put' indicates whether the operation
+ -- succeded.
+ put :: forall d. (AncestorFunctor a d) => x -> Trampoline d m Bool,
+ -- | Function 'canPut' checks if the argument `Sink` accepts values, i.e., whether a 'put' operation would succeed on
+ -- the sink.
+ canPut :: forall d. (AncestorFunctor a d) => Trampoline d m Bool
+ }
+
+-- | A 'Source' can be used to read values into any nested `Trampoline` computation whose functor provably descends from
+-- the functor /a/. It's the read-only end of a 'Pipe' communication channel.
+newtype Source (m :: * -> *) a x =
+ Source
+ {
+ -- | Function 'get' tries to get a value from the given 'Source' argument. The intervening 'Trampoline' computations
+ -- suspend all the way to the 'pipe' function invocation that created the source. The function returns 'Nothing' if
+ -- the argument source is empty.
+ get :: forall d. (AncestorFunctor a d) => Trampoline d m (Maybe x)
+ }
+
+-- | Converts a 'Sink' on the ancestor functor /a/ into a sink on the descendant functor /d/.
+liftSink :: forall m a d x. (Monad m, AncestorFunctor a d) => Sink m a x -> Sink m d x
+liftSink s = Sink {put= liftOut . (put s :: x -> Trampoline d m Bool),
+ canPut= liftOut (canPut s :: Trampoline d m Bool)}
+
+-- | Converts a 'Source' on the ancestor functor /a/ into a source on the descendant functor /d/.
+liftSource :: forall m a d x. (Monad m, AncestorFunctor a d) => Source m a x -> Source m d x
+liftSource s = Source {get= liftOut (get s :: Trampoline d m (Maybe x))}
+
+-- | The 'pipe' function splits the computation into two concurrent parts, /producer/ and /consumer/. The /producer/ is
+-- given a 'Sink' to put values into, and /consumer/ a 'Source' to get those values from. Once producer and consumer
+-- both complete, 'pipe' returns their paired results.
+pipe :: forall m a a1 a2 x r1 r2. (Monad m, Functor a, a1 ~ SinkFunctor a x, a2 ~ SourceFunctor a x) =>
+ (Sink m a1 x -> Trampoline a1 m r1) -> (Source m a2 x -> Trampoline a2 m r2) -> Trampoline a m (r1, r2)
+pipe producer consumer = coupleNestedFiniteSequential (producer sink) (consumer source) where
+ sink = Sink {put= liftOut . (local . tryYield :: x -> Trampoline a1 m Bool),
+ canPut= liftOut (local canYield :: Trampoline a1 m Bool)} :: Sink m a1 x
+ source = Source (liftOut (local await :: Trampoline a2 m (Maybe x))) :: Source m a2 x
+
+-- | The 'pipeP' function is equivalent to 'pipe', except the /producer/ and /consumer/ are run in parallel.
+pipeP :: forall m a a1 a2 x r1 r2. (ParallelizableMonad m, Functor a, a1 ~ SinkFunctor a x, a2 ~ SourceFunctor a x) =>
+ (Sink m a1 x -> Trampoline a1 m r1) -> (Source m a2 x -> Trampoline a2 m r2) -> Trampoline a m (r1, r2)
+pipeP producer consumer = coupleNestedFinite (producer sink) (consumer source) where
+ sink = Sink {put= liftOut . (local . tryYield :: x -> Trampoline a1 m Bool),
+ canPut= liftOut (local canYield :: Trampoline a1 m Bool)} :: Sink m a1 x
+ source = Source (liftOut (local await :: Trampoline a2 m (Maybe x))) :: Source m a2 x
+
+-- | The 'pipePS' function acts either as 'pipeP' or as 'pipe', depending on the argument /parallel/.
+pipePS :: forall m a a1 a2 x r1 r2. (ParallelizableMonad m, Functor a, a1 ~ SinkFunctor a x, a2 ~ SourceFunctor a x) =>
+ Bool -> (Sink m a1 x -> Trampoline a1 m r1) -> (Source m a2 x -> Trampoline a2 m r2) ->
+ Trampoline a m (r1, r2)
+pipePS parallel = if parallel then pipeP else pipe
+
+getSuccess :: forall m a d x . (Monad m, AncestorFunctor a d)
+ => Source m a x -> (x -> Trampoline d m ()) {- ^ Success continuation -} -> Trampoline d m ()
+getSuccess source succeed = get source >>= maybe (return ()) succeed
+
+-- | Function 'get'' assumes that the argument source is not empty and returns the value the source yields. If the
+-- source is empty, the function throws an error.
+get' :: forall m a d x . (Monad m, AncestorFunctor a d) => Source m a x -> Trampoline d m x
+get' source = get source >>= maybe (error "get' failed") return
+
+-- | 'pour' copies all data from the /source/ argument into the /sink/ argument, as long as there is anything to copy
+-- and the sink accepts it.
+pour :: forall m a1 a2 d x . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d)
+ => Source m a1 x -> Sink m a2 x -> Trampoline d m ()
+pour source sink = fill'
+ where fill' = canPut sink >>= flip when (getSuccess source (\x-> put sink x >> fill'))
+
+-- | 'pourMap' is like 'pour' that applies the function /f/ to each argument before passing it into the /sink/.
+pourMap :: forall m a1 a2 d x y . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d)
+ => (x -> y) -> Source m a1 x -> Sink m a2 y -> Trampoline d m ()
+pourMap f source sink = loop
+ where loop = canPut sink >>= flip when (get source >>= maybe (return ()) (\x-> put sink (f x) >> loop))
+
+-- | 'pourMapMaybe' is to 'pourMap' like 'Data.Maybe.mapMaybe' is to 'Data.List.Map'.
+pourMapMaybe :: forall m a1 a2 d x y . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d)
+ => (x -> Maybe y) -> Source m a1 x -> Sink m a2 y -> Trampoline d m ()
+pourMapMaybe f source sink = loop
+ where loop = canPut sink >>= flip when (get source >>= maybe (return ()) (\x-> maybe (return False) (put sink) (f x) >> loop))
+
+-- | 'tee' is similar to 'pour' except it distributes every input value from the /source/ arguments into both /sink1/
+-- and /sink2/.
+tee :: forall m a1 a2 a3 d x . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d)
+ => Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Trampoline d m ()
+tee source sink1 sink2 = distribute
+ where distribute = do c1 <- canPut sink1
+ c2 <- canPut sink2
+ when (c1 && c2)
+ (get source >>= maybe (return ()) (\x-> put sink1 x >> put sink2 x >> distribute))
+
+-- | 'putList' puts entire list into its /sink/ argument, as long as the sink accepts it. The remainder that wasn't
+-- accepted by the sink is the result value.
+putList :: forall m a d x. (Monad m, AncestorFunctor a d) => [x] -> Sink m a x -> Trampoline d m [x]
+putList [] sink = return []
+putList l@(x:rest) sink = put sink x >>= cond (putList rest sink) (return l)
+
+-- | 'getList' returns the list of all values generated by the source.
+getList :: forall m a d x. (Monad m, AncestorFunctor a d) => Source m a x -> Trampoline d m [x]
+getList source = getList' return
+ where getList' f = get source >>= maybe (f []) (\x-> getList' (f . (x:)))
+
+-- | 'consumeAndSuppress' consumes the entire source ignoring the values it generates.
+consumeAndSuppress :: forall m a d x. (Monad m, AncestorFunctor a d) => Source m a x -> Trampoline d m ()
+consumeAndSuppress source = get source
+ >>= maybe (return ()) (const (consumeAndSuppress source))
+
+-- | A utility function wrapping if-then-else, useful for handling monadic truth values
+cond :: a -> a -> Bool -> a
+cond x y test = if test then x else y
+
+-- | A utility function, useful for handling monadic list values where empty list means success
+whenNull :: forall a m. Monad m => m [a] -> [a] -> m [a]
+whenNull action list = if null list then action else return list
+
+-- | Like 'putList', except it puts the contents of the given 'Data.Sequence.Seq' into the sink.
+putQueue :: forall m a d x. (Monad m, AncestorFunctor a d) => Seq x -> Sink m a x -> Trampoline d m [x]
+putQueue q sink = putList (toList (viewl q)) sink