scc 0.4 → 0.5
raw patch · 11 files changed
+1679/−2127 lines, 11 filesdep +monad-coroutinedep +monad-paralleldep −parallelPVP ok
version bump matches the API change (PVP)
Dependencies added: monad-coroutine, monad-parallel
Dependencies removed: parallel
API changes (from Hackage documentation)
- Control.Concurrent.Coroutine: Await :: !x -> y -> Await x y
- Control.Concurrent.Coroutine: Both :: (NestedFunctor l r x) -> SomeFunctor l r x
- Control.Concurrent.Coroutine: LeftF :: (l x) -> EitherFunctor l r x
- Control.Concurrent.Coroutine: LeftSome :: (l x) -> SomeFunctor l r x
- Control.Concurrent.Coroutine: NestedFunctor :: (l (r x)) -> NestedFunctor l r x
- Control.Concurrent.Coroutine: RightF :: (r x) -> EitherFunctor l r x
- Control.Concurrent.Coroutine: RightSome :: (r x) -> SomeFunctor l r x
- Control.Concurrent.Coroutine: SeesawResolver :: (forall t. s1 t -> t) -> (forall t. s2 t -> t) -> (forall t1 t2 r. (t1 -> r) -> (t2 -> r) -> (t1 -> t2 -> r) -> s1 t1 -> s2 t2 -> r) -> SeesawResolver s1 s2
- Control.Concurrent.Coroutine: Yield :: x -> y -> Yield x y
- Control.Concurrent.Coroutine: await :: (Monad m) => Coroutine (Await x) m x
- Control.Concurrent.Coroutine: bindM2 :: (ParallelizableMonad m) => (a -> b -> m c) -> m a -> m b -> m c
- Control.Concurrent.Coroutine: class (Functor a, Functor d) => AncestorFunctor a d
- Control.Concurrent.Coroutine: class (Monad m) => ParallelizableMonad m
- Control.Concurrent.Coroutine: couple :: (Monad m, Functor s1, Functor s2) => (forall x y r. (x -> y -> m r) -> m x -> m y -> m r) -> Coroutine s1 m x -> Coroutine s2 m y -> Coroutine (SomeFunctor s1 s2) m (x, y)
- Control.Concurrent.Coroutine: coupleNested :: (Monad m, Functor s0, Functor s1, Functor s2) => (forall x y r. (x -> y -> m r) -> m x -> m y -> m r) -> Coroutine (EitherFunctor s0 s1) m x -> Coroutine (EitherFunctor s0 s2) m y -> Coroutine (EitherFunctor s0 (SomeFunctor s1 s2)) m (x, y)
- Control.Concurrent.Coroutine: data Await x y
- Control.Concurrent.Coroutine: data Coroutine s m r
- Control.Concurrent.Coroutine: data EitherFunctor l r x
- Control.Concurrent.Coroutine: data Naught x
- Control.Concurrent.Coroutine: data SeesawResolver s1 s2
- Control.Concurrent.Coroutine: data SomeFunctor l r x
- Control.Concurrent.Coroutine: data Yield x y
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor a) => AncestorFunctor a a
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor l, Functor r) => Functor (EitherFunctor l r)
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor l, Functor r) => Functor (NestedFunctor l r)
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor l, Functor r) => Functor (SomeFunctor l r)
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor s) => MonadTrans (Coroutine s)
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor s, Monad m) => Monad (Coroutine s m)
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor s, MonadIO m) => MonadIO (Coroutine s m)
- Control.Concurrent.Coroutine: instance [overlap ok] (Functor s, ParallelizableMonad m) => ParallelizableMonad (Coroutine s m)
- Control.Concurrent.Coroutine: instance [overlap ok] (d ~ EitherFunctor d' s, Functor a, Functor d', Functor d, AncestorFunctor a d') => AncestorFunctor a d
- Control.Concurrent.Coroutine: instance [overlap ok] Functor (Await x)
- Control.Concurrent.Coroutine: instance [overlap ok] Functor (Yield x)
- Control.Concurrent.Coroutine: instance [overlap ok] Functor Naught
- Control.Concurrent.Coroutine: instance [overlap ok] ParallelizableMonad IO
- Control.Concurrent.Coroutine: instance [overlap ok] ParallelizableMonad Identity
- Control.Concurrent.Coroutine: instance [overlap ok] ParallelizableMonad Maybe
- Control.Concurrent.Coroutine: liftOut :: (Monad m, Functor a, AncestorFunctor a d) => Coroutine a m x -> Coroutine d m x
- Control.Concurrent.Coroutine: local :: (Functor r, Monad m) => Coroutine r m x -> Coroutine (EitherFunctor l r) m x
- Control.Concurrent.Coroutine: nest :: (Functor a, Functor b) => a x -> b y -> NestedFunctor a b (x, y)
- Control.Concurrent.Coroutine: newtype NestedFunctor l r x
- Control.Concurrent.Coroutine: out :: (Functor l, Monad m) => Coroutine l m x -> Coroutine (EitherFunctor l r) m x
- Control.Concurrent.Coroutine: pogoStick :: (Functor s, Monad m) => (s (Coroutine s m x) -> Coroutine s m x) -> Coroutine s m x -> m x
- Control.Concurrent.Coroutine: pogoStickNested :: (Functor s1, Functor s2, Monad m) => (s2 (Coroutine (EitherFunctor s1 s2) m x) -> Coroutine (EitherFunctor s1 s2) m x) -> Coroutine (EitherFunctor s1 s2) m x -> Coroutine s1 m x
- Control.Concurrent.Coroutine: resumeAny :: SeesawResolver s1 s2 -> forall t1 t2 r. (t1 -> r) -> (t2 -> r) -> (t1 -> t2 -> r) -> s1 t1 -> s2 t2 -> r
- Control.Concurrent.Coroutine: resumeLeft :: SeesawResolver s1 s2 -> forall t. s1 t -> t
- Control.Concurrent.Coroutine: resumeRight :: SeesawResolver s1 s2 -> forall t. s2 t -> t
- Control.Concurrent.Coroutine: runCoroutine :: (Monad m) => Coroutine Naught m x -> m x
- Control.Concurrent.Coroutine: seesaw :: (Monad m, Functor s1, Functor s2) => (forall x y r. (x -> y -> m r) -> m x -> m y -> m r) -> SeesawResolver s1 s2 -> Coroutine s1 m x -> Coroutine s2 m y -> m (x, y)
- Control.Concurrent.Coroutine: seesawNested :: (Monad m, Functor s0, Functor s1, Functor s2) => (forall x y r. (x -> y -> m r) -> m x -> m y -> m r) -> SeesawResolver s1 s2 -> Coroutine (EitherFunctor s0 s1) m x -> Coroutine (EitherFunctor s0 s2) m y -> Coroutine s0 m (x, y)
- Control.Concurrent.Coroutine: suspend :: (Monad m, Functor s) => s (Coroutine s m x) -> Coroutine s m x
- Control.Concurrent.Coroutine: yield :: (Monad m) => x -> Coroutine (Yield x) m ()
- Control.Concurrent.SCC.Combinators: connect :: (PipeableComponentPair m w c1 c2 c3) => Bool -> c1 -> c2 -> c3
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ConsumerType ()) (ConsumerType ()) (ConsumerType ()) m [x] () (Consumer m x ()) (Consumer m x ()) (Consumer m x ())
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ConsumerType ()) (ProducerType ()) TransducerType m [x] [y] (Consumer m x ()) (Producer m y ()) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ConsumerType ()) TransducerType TransducerType m [x] [y] (Consumer m x ()) (Transducer m x y) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ConsumerType r1) (PerformerType r2) (ConsumerType r2) m [x] () (Consumer m x r1) (Performer m r2) (Consumer m x r2)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (PerformerType r) TransducerType TransducerType m [x] [y] (Performer m r) (Transducer m x y) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (PerformerType r1) (ConsumerType r2) (ConsumerType r2) m [x] () (Performer m r1) (Consumer m x r2) (Consumer m x r2)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (PerformerType r1) (PerformerType r2) (PerformerType r2) m () () (Performer m r1) (Performer m r2) (Performer m r2)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (PerformerType r1) (ProducerType r2) (ProducerType r2) m () [x] (Performer m r1) (Producer m x r2) (Producer m x r2)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ProducerType ()) (ConsumerType ()) TransducerType m [x] [y] (Producer m y ()) (Consumer m x ()) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ProducerType ()) TransducerType TransducerType m [x] [y] (Producer m y ()) (Transducer m x y) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair (ProducerType r1) (PerformerType r2) (ProducerType r2) m () [x] (Producer m x r1) (Performer m r2) (Producer m x r2)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair TransducerType (ConsumerType ()) TransducerType m [x] [y] (Transducer m x y) (Consumer m x ()) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair TransducerType (PerformerType r) TransducerType m [x] [y] (Transducer m x y) (Performer m r) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair TransducerType (ProducerType ()) TransducerType m [x] [y] (Transducer m x y) (Producer m y ()) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => JoinableComponentPair TransducerType TransducerType TransducerType m [x] [y] (Transducer m x y) (Transducer m x y) (Transducer m x y)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => PipeableComponentPair m x (Producer m x ()) (Consumer m x ()) (Performer m ())
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => PipeableComponentPair m x (Producer m x r) (Transducer m x y) (Producer m y r)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => PipeableComponentPair m y (Transducer m x y) (Consumer m y r) (Consumer m x r)
- Control.Concurrent.SCC.Combinators: instance (ParallelizableMonad m) => PipeableComponentPair m y (Transducer m x y) (Transducer m y z) (Transducer m x z)
- Control.Concurrent.SCC.Components: asis :: (Monad m) => TransducerComponent m x x
- Control.Concurrent.SCC.Primitives: asis :: (Monad m) => Transducer m x x
- Control.Concurrent.SCC.Streams: consumeAndSuppress :: (Monad m, AncestorFunctor a d) => Source m a x -> Coroutine d m ()
- Control.Concurrent.SCC.Streams: get' :: (Monad m, AncestorFunctor a d) => Source m a x -> Coroutine d m x
- Control.Concurrent.SCC.Streams: getSuccess :: (Monad m, AncestorFunctor a d) => Source m a x -> (x -> Coroutine d m ()) -> Coroutine d m ()
- Control.Concurrent.SCC.Streams: pourMap :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => (x -> y) -> Source m a1 x -> Sink m a2 y -> Coroutine d m ()
- Control.Concurrent.SCC.Streams: whenNull :: (Monad m) => m [a] -> [a] -> m [a]
- Control.Concurrent.SCC.Types: foldingTransducer :: (Monad m) => (s -> x -> s) -> s -> (s -> y) -> Transducer m x y
- Control.Concurrent.SCC.Types: instance (Monad m) => Branching (Consumer m x ()) m x [x]
- Control.Concurrent.SCC.Types: instance (Monad m) => Branching (Transducer m x y) m x [x]
- Control.Concurrent.SCC.Types: instance (ParallelizableMonad m) => Branching (Splitter m x b) m x [x]
+ Control.Concurrent.SCC.Combinators: compose :: (PipeableComponentPair m w c1 c2 c3) => Bool -> c1 -> c2 -> c3
+ Control.Concurrent.SCC.Combinators: findsFalseIn :: (Monad m, AncestorFunctor a d) => Splitter m x b -> Source m a x -> Coroutine d m Bool
+ Control.Concurrent.SCC.Combinators: findsTrueIn :: (Monad m, AncestorFunctor a d) => Splitter m x b -> Source m a x -> Coroutine d m (Maybe (Maybe b))
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ConsumerType ()) (ConsumerType ()) (ConsumerType ()) m [x] () (Consumer m x ()) (Consumer m x ()) (Consumer m x ())
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ConsumerType ()) (ProducerType ()) TransducerType m [x] [y] (Consumer m x ()) (Producer m y ()) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ConsumerType ()) TransducerType TransducerType m [x] [y] (Consumer m x ()) (Transducer m x y) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ConsumerType r1) (PerformerType r2) (ConsumerType r2) m [x] () (Consumer m x r1) (Performer m r2) (Consumer m x r2)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (PerformerType r) TransducerType TransducerType m [x] [y] (Performer m r) (Transducer m x y) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (PerformerType r1) (ConsumerType r2) (ConsumerType r2) m [x] () (Performer m r1) (Consumer m x r2) (Consumer m x r2)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (PerformerType r1) (PerformerType r2) (PerformerType r2) m () () (Performer m r1) (Performer m r2) (Performer m r2)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (PerformerType r1) (ProducerType r2) (ProducerType r2) m () [x] (Performer m r1) (Producer m x r2) (Producer m x r2)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ProducerType ()) (ConsumerType ()) TransducerType m [x] [y] (Producer m y ()) (Consumer m x ()) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ProducerType ()) TransducerType TransducerType m [x] [y] (Producer m y ()) (Transducer m x y) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair (ProducerType r1) (PerformerType r2) (ProducerType r2) m () [x] (Producer m x r1) (Performer m r2) (Producer m x r2)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair TransducerType (ConsumerType ()) TransducerType m [x] [y] (Transducer m x y) (Consumer m x ()) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair TransducerType (PerformerType r) TransducerType m [x] [y] (Transducer m x y) (Performer m r) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair TransducerType (ProducerType ()) TransducerType m [x] [y] (Transducer m x y) (Producer m y ()) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => JoinableComponentPair TransducerType TransducerType TransducerType m [x] [y] (Transducer m x y) (Transducer m x y) (Transducer m x y)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => PipeableComponentPair m x (Producer m x ()) (Consumer m x ()) (Performer m ())
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => PipeableComponentPair m x (Producer m x r) (Transducer m x y) (Producer m y r)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => PipeableComponentPair m y (Transducer m x y) (Consumer m y r) (Consumer m x r)
+ Control.Concurrent.SCC.Combinators: instance (MonadParallel m) => PipeableComponentPair m y (Transducer m x y) (Transducer m y z) (Transducer m x z)
+ Control.Concurrent.SCC.Combinators: teeConsumers :: (MonadParallel m) => Bool -> (forall a. OpenConsumer m a (SinkFunctor d x) x r1) -> (forall a. OpenConsumer m a (SourceFunctor d x) x r2) -> OpenConsumer m a d x (r1, r2)
+ Control.Concurrent.SCC.Components: id :: (Monad m) => TransducerComponent m x x
+ Control.Concurrent.SCC.Streams: class (Functor a, Functor d) => AncestorFunctor a :: (* -> *) d :: (* -> *)
+ Control.Concurrent.SCC.Streams: filterMSink :: (Monad m) => (forall d. (AncestorFunctor a d) => x -> Coroutine d m Bool) -> Sink m a x -> Sink m a x
+ Control.Concurrent.SCC.Streams: filterMSource :: (Monad m) => (forall d. (AncestorFunctor a d) => x -> Coroutine d m Bool) -> Source m a x -> Source m a x
+ Control.Concurrent.SCC.Streams: filterMStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => (x -> Coroutine d m Bool) -> Source m a1 x -> Sink m a2 x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: foldMStream :: (Monad m, AncestorFunctor a d) => (acc -> x -> Coroutine d m acc) -> acc -> Source m a x -> Coroutine d m acc
+ Control.Concurrent.SCC.Streams: foldMStream_ :: (Monad m, AncestorFunctor a d) => (acc -> x -> Coroutine d m acc) -> acc -> Source m a x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: foldStream :: (Monad m, AncestorFunctor a d) => (acc -> x -> acc) -> acc -> Source m a x -> Coroutine d m acc
+ Control.Concurrent.SCC.Streams: get :: Source m a x -> forall d. (AncestorFunctor a d) => Coroutine d m (Maybe x)
+ Control.Concurrent.SCC.Streams: getWith :: (Monad m, AncestorFunctor a d) => (x -> Coroutine d m ()) -> Source m a x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: mapAccumStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => (acc -> x -> (acc, y)) -> acc -> Source m a1 x -> Sink m a2 y -> Coroutine d m acc
+ Control.Concurrent.SCC.Streams: mapMSink :: (Monad m) => (forall d. (AncestorFunctor a d) => x -> Coroutine d m y) -> Sink m a y -> Sink m a x
+ Control.Concurrent.SCC.Streams: mapMSource :: (Monad m) => (forall d. (AncestorFunctor a d) => x -> Coroutine d m y) -> Source m a x -> Source m a y
+ Control.Concurrent.SCC.Streams: mapMStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => (x -> Coroutine d m y) -> Source m a1 x -> Sink m a2 y -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: mapMStream_ :: (Monad m, AncestorFunctor a d) => (x -> Coroutine d m ()) -> Source m a x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: mapMaybeSink :: (Monad m) => (x -> Maybe y) -> Sink m a y -> Sink m a x
+ Control.Concurrent.SCC.Streams: mapMaybeSource :: (Monad m) => (x -> Maybe y) -> Source m a x -> Source m a y
+ Control.Concurrent.SCC.Streams: mapMaybeStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => (x -> Maybe y) -> Source m a1 x -> Sink m a2 y -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: mapSink :: (Monad m) => (x -> y) -> Sink m a y -> Sink m a x
+ Control.Concurrent.SCC.Streams: mapSource :: (Monad m) => (x -> y) -> Source m a x -> Source m a y
+ Control.Concurrent.SCC.Streams: mapStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => (x -> y) -> Source m a1 x -> Sink m a2 y -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: nullSink :: (Monad m) => Sink m a x
+ Control.Concurrent.SCC.Streams: nullSource :: (Monad m) => Source m a x
+ Control.Concurrent.SCC.Streams: parZipWithMStream :: (MonadParallel m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d) => (x -> y -> Coroutine d m z) -> Source m a1 x -> Source m a2 y -> Sink m a3 z -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: partitionStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d) => (x -> Bool) -> Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: put :: Sink m a x -> forall d. (AncestorFunctor a d) => x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: teeSink :: (Monad m, AncestorFunctor a1 a3, AncestorFunctor a2 a3) => Sink m a1 x -> Sink m a2 x -> Sink m a3 x
+ Control.Concurrent.SCC.Streams: teeSource :: (Monad m, AncestorFunctor a1 a3, AncestorFunctor a2 a3) => Sink m a1 x -> Source m a2 x -> Source m a3 x
+ Control.Concurrent.SCC.Streams: unfoldMStream :: (Monad m, AncestorFunctor a d) => (acc -> Coroutine d m (Maybe (x, acc))) -> acc -> Sink m a x -> Coroutine d m acc
+ Control.Concurrent.SCC.Streams: unmapMStream_ :: (Monad m, AncestorFunctor a d) => Coroutine d m (Maybe x) -> Sink m a x -> Coroutine d m ()
+ Control.Concurrent.SCC.Streams: zipWithMStream :: (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d) => (x -> y -> Coroutine d m z) -> Source m a1 x -> Source m a2 y -> Sink m a3 z -> Coroutine d m ()
+ Control.Concurrent.SCC.Types: (<|<) :: (MonadParallel m) => Transducer m y z -> Transducer m x y -> Transducer m x z
+ Control.Concurrent.SCC.Types: (>|>) :: (MonadParallel m) => Transducer m x y -> Transducer m y z -> Transducer m x z
+ Control.Concurrent.SCC.Types: instance (Monad m) => Branching (Transducer m x y) m x ()
+ Control.Concurrent.SCC.Types: instance (Monad m) => Category (Transducer m)
+ Control.Concurrent.SCC.Types: instance (MonadParallel m) => Branching (Splitter m x b) m x ()
- Control.Concurrent.SCC.Combinators: between :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1
+ Control.Concurrent.SCC.Combinators: between :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1
- Control.Concurrent.SCC.Combinators: followedBy :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)
+ Control.Concurrent.SCC.Combinators: followedBy :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)
- Control.Concurrent.SCC.Combinators: foreach :: (ParallelizableMonad m, Branching c m x [x]) => Bool -> Splitter m x b -> c -> c -> c
+ Control.Concurrent.SCC.Combinators: foreach :: (MonadParallel m, Branching c m x ()) => Bool -> Splitter m x b -> c -> c -> c
- Control.Concurrent.SCC.Combinators: having :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1
+ Control.Concurrent.SCC.Combinators: having :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1
- Control.Concurrent.SCC.Combinators: havingOnly :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1
+ Control.Concurrent.SCC.Combinators: havingOnly :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1
- Control.Concurrent.SCC.Combinators: ifs :: (ParallelizableMonad m, Branching c m x [x]) => Bool -> Splitter m x b -> c -> c -> c
+ Control.Concurrent.SCC.Combinators: ifs :: (MonadParallel m, Branching c m x ()) => Bool -> Splitter m x b -> c -> c -> c
- Control.Concurrent.SCC.Combinators: nestedIn :: (ParallelizableMonad m) => [(Bool, (Splitter m x b, Splitter m x b))] -> Splitter m x b
+ Control.Concurrent.SCC.Combinators: nestedIn :: (MonadParallel m) => [(Bool, (Splitter m x b, Splitter m x b))] -> Splitter m x b
- Control.Concurrent.SCC.Combinators: pAnd :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)
+ Control.Concurrent.SCC.Combinators: pAnd :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)
- Control.Concurrent.SCC.Combinators: pOr :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)
+ Control.Concurrent.SCC.Combinators: pOr :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)
- Control.Concurrent.SCC.Combinators: sAnd :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)
+ Control.Concurrent.SCC.Combinators: sAnd :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)
- Control.Concurrent.SCC.Combinators: sOr :: (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)
+ Control.Concurrent.SCC.Combinators: sOr :: (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)
- Control.Concurrent.SCC.Combinators: unless :: (ParallelizableMonad m) => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x
+ Control.Concurrent.SCC.Combinators: unless :: (MonadParallel m) => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x
- Control.Concurrent.SCC.Combinators: wherever :: (ParallelizableMonad m) => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x
+ Control.Concurrent.SCC.Combinators: wherever :: (MonadParallel m) => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x
- Control.Concurrent.SCC.Combinators: while :: (ParallelizableMonad m) => [(Bool, (Transducer m x x, Splitter m x b))] -> Transducer m x x
+ Control.Concurrent.SCC.Combinators: while :: (MonadParallel m) => [(Bool, (Transducer m x x, Splitter m x b))] -> Transducer m x x
- Control.Concurrent.SCC.Components: (&&) :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2)
+ Control.Concurrent.SCC.Components: (&&) :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2)
- Control.Concurrent.SCC.Components: (...) :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1
+ Control.Concurrent.SCC.Components: (...) :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1
- Control.Concurrent.SCC.Components: (>&) :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2)
+ Control.Concurrent.SCC.Components: (>&) :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2)
- Control.Concurrent.SCC.Components: (>|) :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (Either b1 b2)
+ Control.Concurrent.SCC.Components: (>|) :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (Either b1 b2)
- Control.Concurrent.SCC.Components: (||) :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (Either b1 b2)
+ Control.Concurrent.SCC.Components: (||) :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (Either b1 b2)
- Control.Concurrent.SCC.Components: endOf :: (ParallelizableMonad m) => SplitterComponent m x b -> SplitterComponent m x (Maybe b)
+ Control.Concurrent.SCC.Components: endOf :: (MonadParallel m) => SplitterComponent m x b -> SplitterComponent m x (Maybe b)
- Control.Concurrent.SCC.Components: followedBy :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2)
+ Control.Concurrent.SCC.Components: followedBy :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2)
- Control.Concurrent.SCC.Components: foreach :: (ParallelizableMonad m, Branching c m x [x]) => SplitterComponent m x b -> Component c -> Component c -> Component c
+ Control.Concurrent.SCC.Components: foreach :: (MonadParallel m, Branching c m x ()) => SplitterComponent m x b -> Component c -> Component c -> Component c
- Control.Concurrent.SCC.Components: fromList :: (Monad m) => [x] -> ProducerComponent m x [x]
+ Control.Concurrent.SCC.Components: fromList :: (Monad m) => [x] -> ProducerComponent m x ()
- Control.Concurrent.SCC.Components: having :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1
+ Control.Concurrent.SCC.Components: having :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1
- Control.Concurrent.SCC.Components: havingOnly :: (ParallelizableMonad m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1
+ Control.Concurrent.SCC.Components: havingOnly :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1
- Control.Concurrent.SCC.Components: ifs :: (ParallelizableMonad m, Branching c m x [x]) => SplitterComponent m x b -> Component c -> Component c -> Component c
+ Control.Concurrent.SCC.Components: ifs :: (MonadParallel m, Branching c m x ()) => SplitterComponent m x b -> Component c -> Component c -> Component c
- Control.Concurrent.SCC.Components: nestedIn :: (ParallelizableMonad m) => SplitterComponent m x b -> SplitterComponent m x b -> SplitterComponent m x b
+ Control.Concurrent.SCC.Components: nestedIn :: (MonadParallel m) => SplitterComponent m x b -> SplitterComponent m x b -> SplitterComponent m x b
- Control.Concurrent.SCC.Components: parseNestedRegions :: (ParallelizableMonad m) => SplitterComponent m x (Boundary b) -> ParserComponent m x b
+ Control.Concurrent.SCC.Components: parseNestedRegions :: (MonadParallel m) => SplitterComponent m x (Boundary b) -> ParserComponent m x b
- Control.Concurrent.SCC.Components: unless :: (ParallelizableMonad m) => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x
+ Control.Concurrent.SCC.Components: unless :: (MonadParallel m) => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x
- Control.Concurrent.SCC.Components: wherever :: (ParallelizableMonad m) => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x
+ Control.Concurrent.SCC.Components: wherever :: (MonadParallel m) => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x
- Control.Concurrent.SCC.Components: while :: (ParallelizableMonad m) => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x
+ Control.Concurrent.SCC.Components: while :: (MonadParallel m) => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x
- Control.Concurrent.SCC.Components: xmlElementHavingTag :: (ParallelizableMonad m) => SplitterComponent m (Markup Token Char) b -> SplitterComponent m (Markup Token Char) b
+ Control.Concurrent.SCC.Components: xmlElementHavingTag :: (MonadParallel m) => SplitterComponent m (Markup Token Char) b -> SplitterComponent m (Markup Token Char) b
- Control.Concurrent.SCC.Components: xmlHavingOnlyText :: (ParallelizableMonad m) => SplitterComponent m (Markup Token Char) b1 -> SplitterComponent m Char b2 -> SplitterComponent m (Markup Token Char) b1
+ Control.Concurrent.SCC.Components: xmlHavingOnlyText :: (MonadParallel m) => SplitterComponent m (Markup Token Char) b1 -> SplitterComponent m Char b2 -> SplitterComponent m (Markup Token Char) b1
- Control.Concurrent.SCC.Components: xmlHavingText :: (ParallelizableMonad m) => SplitterComponent m (Markup Token Char) b1 -> SplitterComponent m Char b2 -> SplitterComponent m (Markup Token Char) b1
+ Control.Concurrent.SCC.Components: xmlHavingText :: (MonadParallel m) => SplitterComponent m (Markup Token Char) b1 -> SplitterComponent m Char b2 -> SplitterComponent m (Markup Token Char) b1
- Control.Concurrent.SCC.Primitives: fromList :: (Monad m) => [x] -> Producer m x [x]
+ Control.Concurrent.SCC.Primitives: fromList :: (Monad m) => [x] -> Producer m x ()
- Control.Concurrent.SCC.Streams: pipeP :: (ParallelizableMonad m, Functor a, a1 ~ (SinkFunctor a x), a2 ~ (SourceFunctor a x)) => (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2)
+ Control.Concurrent.SCC.Streams: pipeP :: (MonadParallel m, Functor a, a1 ~ (SinkFunctor a x), a2 ~ (SourceFunctor a x)) => (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2)
- Control.Concurrent.SCC.Streams: pipePS :: (ParallelizableMonad m, Functor a, a1 ~ (SinkFunctor a x), a2 ~ (SourceFunctor a x)) => Bool -> (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2)
+ Control.Concurrent.SCC.Streams: pipePS :: (MonadParallel m, Functor a, a1 ~ (SinkFunctor a x), a2 ~ (SourceFunctor a x)) => Bool -> (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2)
- Control.Concurrent.SCC.Streams: putList :: (Monad m, AncestorFunctor a d) => [x] -> Sink m a x -> Coroutine d m [x]
+ Control.Concurrent.SCC.Streams: putList :: (Monad m, AncestorFunctor a d) => [x] -> Sink m a x -> Coroutine d m ()
- Control.Concurrent.SCC.Streams: putQueue :: (Monad m, AncestorFunctor a d) => Seq x -> Sink m a x -> Coroutine d m [x]
+ Control.Concurrent.SCC.Streams: putQueue :: (Monad m, AncestorFunctor a d) => Seq x -> Sink m a x -> Coroutine d m ()
- Control.Concurrent.SCC.Streams: type SinkFunctor a x = EitherFunctor a (TryYield x)
+ Control.Concurrent.SCC.Streams: type SinkFunctor a x = EitherFunctor a (Yield x)
- Control.Concurrent.SCC.Types: Splitter :: (forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b) -> Splitter m x b
+ Control.Concurrent.SCC.Types: Splitter :: (forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b ()) -> Splitter m x b
- Control.Concurrent.SCC.Types: Transducer :: (forall a1 a2 d. OpenTransducer m a1 a2 d x y) -> Transducer m x y
+ Control.Concurrent.SCC.Types: Transducer :: (forall a1 a2 d. OpenTransducer m a1 a2 d x y ()) -> Transducer m x y
- Control.Concurrent.SCC.Types: isolateSplitter :: (Monad m) => (forall d. (Functor d) => Source m d x -> Sink m d x -> Sink m d x -> Sink m d b -> Coroutine d m [x]) -> Splitter m x b
+ Control.Concurrent.SCC.Types: isolateSplitter :: (Monad m) => (forall d. (Functor d) => Source m d x -> Sink m d x -> Sink m d x -> Sink m d b -> Coroutine d m ()) -> Splitter m x b
- Control.Concurrent.SCC.Types: isolateTransducer :: (Monad m) => (forall d. (Functor d) => Source m d x -> Sink m d y -> Coroutine d m [x]) -> Transducer m x y
+ Control.Concurrent.SCC.Types: isolateTransducer :: (Monad m) => (forall d. (Functor d) => Source m d x -> Sink m d y -> Coroutine d m ()) -> Transducer m x y
- Control.Concurrent.SCC.Types: pipePS :: (ParallelizableMonad m, Functor a, a1 ~ (SinkFunctor a x), a2 ~ (SourceFunctor a x)) => Bool -> (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2)
+ Control.Concurrent.SCC.Types: pipePS :: (MonadParallel m, Functor a, a1 ~ (SinkFunctor a x), a2 ~ (SourceFunctor a x)) => Bool -> (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2)
- Control.Concurrent.SCC.Types: split :: Splitter m x b -> forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b
+ Control.Concurrent.SCC.Types: split :: Splitter m x b -> forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b ()
- Control.Concurrent.SCC.Types: splitInputToConsumers :: (ParallelizableMonad m, d1 ~ (SinkFunctor d x), AncestorFunctor a d) => Bool -> Splitter m x b -> Source m a x -> (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m [x]) -> (Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m [x]) -> Coroutine d m [x]
+ Control.Concurrent.SCC.Types: splitInputToConsumers :: (MonadParallel m, d1 ~ (SinkFunctor d x), AncestorFunctor a d) => Bool -> Splitter m x b -> Source m a x -> (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m ()) -> (Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m ()) -> Coroutine d m ()
- Control.Concurrent.SCC.Types: splitToConsumers :: (Functor d, Monad m, d1 ~ (SinkFunctor d x), AncestorFunctor a (SinkFunctor (SinkFunctor d1 x) b)) => Splitter m x b -> Source m a x -> (Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m r1) -> (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m r2) -> (Source m (SourceFunctor (SinkFunctor d1 x) b) b -> Coroutine (SourceFunctor (SinkFunctor d1 x) b) m r3) -> Coroutine d m ([x], r1, r2, r3)
+ Control.Concurrent.SCC.Types: splitToConsumers :: (Functor d, Monad m, d1 ~ (SinkFunctor d x), AncestorFunctor a (SinkFunctor (SinkFunctor d1 x) b)) => Splitter m x b -> Source m a x -> (Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m r1) -> (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m r2) -> (Source m (SourceFunctor (SinkFunctor d1 x) b) b -> Coroutine (SourceFunctor (SinkFunctor d1 x) b) m r3) -> Coroutine d m ((), r1, r2, r3)
- Control.Concurrent.SCC.Types: transduce :: Transducer m x y -> forall a1 a2 d. OpenTransducer m a1 a2 d x y
+ Control.Concurrent.SCC.Types: transduce :: Transducer m x y -> forall a1 a2 d. OpenTransducer m a1 a2 d x y ()
- Control.Concurrent.SCC.Types: type OpenSplitter m a1 a2 a3 a4 d x b = (AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d, AncestorFunctor a4 d) => Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Sink m a4 b -> Coroutine d m [x]
+ Control.Concurrent.SCC.Types: type OpenSplitter m a1 a2 a3 a4 d x b r = (AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d, AncestorFunctor a4 d) => Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Sink m a4 b -> Coroutine d m r
- Control.Concurrent.SCC.XML: elementHavingTag :: (ParallelizableMonad m) => Splitter m (Markup Token Char) b -> Splitter m (Markup Token Char) b
+ Control.Concurrent.SCC.XML: elementHavingTag :: (MonadParallel m) => Splitter m (Markup Token Char) b -> Splitter m (Markup Token Char) b
- Control.Concurrent.SCC.XML: havingOnlyText :: (ParallelizableMonad m) => Bool -> Splitter m (Markup Token Char) b1 -> Splitter m Char b2 -> Splitter m (Markup Token Char) b1
+ Control.Concurrent.SCC.XML: havingOnlyText :: (MonadParallel m) => Bool -> Splitter m (Markup Token Char) b1 -> Splitter m Char b2 -> Splitter m (Markup Token Char) b1
- Control.Concurrent.SCC.XML: havingText :: (ParallelizableMonad m) => Bool -> Splitter m (Markup Token Char) b1 -> Splitter m Char b2 -> Splitter m (Markup Token Char) b1
+ Control.Concurrent.SCC.XML: havingText :: (MonadParallel m) => Bool -> Splitter m (Markup Token Char) b1 -> Splitter m Char b2 -> Splitter m (Markup Token Char) b1
Files
- Control/Concurrent/Coroutine.hs +0/−318
- Control/Concurrent/SCC/Combinators.hs +946/−1100
- Control/Concurrent/SCC/Components.hs +31/−27
- Control/Concurrent/SCC/Primitives.hs +46/−119
- Control/Concurrent/SCC/Streams.hs +234/−92
- Control/Concurrent/SCC/Types.hs +67/−90
- Control/Concurrent/SCC/XML.hs +263/−293
- Makefile +23/−13
- Shell.hs +17/−22
- Test.hs +42/−43
- scc.cabal +10/−10
− Control/Concurrent/Coroutine.hs
@@ -1,318 +0,0 @@-{- - Copyright 2009-2010 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/>.--}---- | This module defines the 'Coroutine' monad transformer.--- --- A 'Coroutine' monadic computation can 'suspend' its execution at any time, returning to its invoker. The returned--- coroutine suspension contains the continuation of the coroutine embedded in a functor. Here is an example of a--- coroutine that suspends computation in the 'IO' monad using the functor 'Yield':--- --- @--- producer = do yield 1--- lift (putStrLn \"Produced one, next is four.\")--- yield 4--- return \"Finished\"--- @--- --- A suspended 'Coroutine' computation can be resumed. The easiest way to run a coroutine is by using the 'pogoStick'--- function, which keeps resuming the coroutine in trampolined style until it completes. Here is an example of--- 'pogoStick' applied to the /producer/ above:--- --- @--- printProduce :: Show x => Coroutine (Yield x) IO r -> IO r--- printProduce producer = pogoStick (\\(Yield x cont) -> lift (print x) >> cont) producer--- @--- --- Multiple concurrent coroutines can be run as well, and this module provides two different ways. The function 'seesaw'--- can be used to run two interleaved computations. Another possible way is to weave together steps of different--- coroutines into a single coroutine using the function 'couple', which can then be executed by 'pogoStick'.--- --- Coroutines can be run from within another coroutine. In this case, the nested coroutines would normally suspend to--- their invoker. Another option is to allow a nested coroutine to suspend both itself and its invoker at once. In this--- case, the two suspension functors should be grouped into an 'EitherFunctor'. To run nested coroutines of this kind,--- use functions 'pogoStickNested', 'seesawNested', and 'coupleNested'.--- --- For other uses of trampoline-style coroutines, see--- --- > Trampolined Style - Ganz, S. E. Friedman, D. P. Wand, M, ACM SIGPLAN NOTICES, 1999, VOL 34; NUMBER 9, pages 18-27--- --- and--- --- > The Essence of Multitasking - William L. Harrison, Proceedings of the 11th International Conference on Algebraic--- > Methodology and Software Technology, volume 4019 of Lecture Notes in Computer Science, 2006--{-# LANGUAGE ScopedTypeVariables, Rank2Types, MultiParamTypeClasses, TypeFamilies, EmptyDataDecls,- FlexibleInstances, OverlappingInstances, UndecidableInstances- #-}--module Control.Concurrent.Coroutine- (- -- * Coroutine definition- Coroutine,- suspend,- -- * Useful classes- ParallelizableMonad(..), AncestorFunctor,- -- * Running Coroutine computations- runCoroutine, pogoStick, pogoStickNested, seesaw, seesawNested, SeesawResolver(..),- -- * Suspension functors- Yield(Yield), Await(Await), Naught,- yield, await,- -- * Nested and coupled Coroutine computations- nest, couple, coupleNested,- local, out, liftOut,- EitherFunctor(LeftF, RightF), NestedFunctor (NestedFunctor), SomeFunctor(..)- )-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(..), MonadIO(..))-import Control.Parallel (par, pseq)---- | Class of monads that can perform two computations in parallel.-class Monad m => ParallelizableMonad m where- -- | Perform two monadic computations in parallel and pass the results.- bindM2 :: (a -> b -> m c) -> m a -> m b -> m c- bindM2 f ma mb = do {a <- ma; b <- mb; f a b}---- | Any monad that allows the result value to be extracted, such as `Identity` or `Maybe` monad, can implement--- `bindM2` by using `par`.-instance ParallelizableMonad Identity where- bindM2 f ma mb = let a = runIdentity ma- b = runIdentity mb- in a `par` (b `pseq` a `pseq` f a b)--instance ParallelizableMonad Maybe where- bindM2 f ma mb = case ma `par` (mb `pseq` (ma, mb))- of (Just a, Just b) -> f a b- _ -> Nothing---- | IO is parallelizable by `forkIO`.-instance ParallelizableMonad IO where- bindM2 f ma mb = do va <- newEmptyMVar- vb <- newEmptyMVar- forkIO (ma >>= putMVar va)- forkIO (mb >>= putMVar vb)- a <- takeMVar va- b <- takeMVar vb- f a b---- | Suspending, resumable monadic computations.-newtype Coroutine s m r = Coroutine {- -- | Run the next step of a `Coroutine` computation.- resume :: m (CoroutineState s m r)- }--data CoroutineState s m r =- -- | Coroutine computation is finished with final value /r/.- Done r- -- | Computation is suspended, its remainder is embedded in the functor /s/.- | Suspend! (s (Coroutine s m r))--instance (Functor s, Monad m) => Monad (Coroutine s m) where- return x = Coroutine (return (Done x))- t >>= f = Coroutine (resume t >>= apply f)- where apply f (Done x) = resume (f x)- apply f (Suspend s) = return (Suspend (fmap (>>= f) s))--instance (Functor s, ParallelizableMonad m) => ParallelizableMonad (Coroutine s m) where- bindM2 f t1 t2 = Coroutine (bindM2 combine (resume t1) (resume t2)) where- combine (Done x) (Done y) = resume (f x y)- combine (Suspend s) (Done y) = return $ Suspend (fmap (flip f y =<<) s)- combine (Done x) (Suspend s) = return $ Suspend (fmap (f x =<<) s)- combine (Suspend s1) (Suspend s2) = return $ Suspend (fmap (bindM2 f $ suspend s1) s2)--instance Functor s => MonadTrans (Coroutine s) where- lift = Coroutine . liftM Done--instance (Functor s, MonadIO m) => MonadIO (Coroutine s m) where- liftIO = lift . liftIO---- | The 'Yield' functor instance is equivalent to (,) but more descriptive.-data Yield x y = Yield x y-instance Functor (Yield x) where- fmap f (Yield x y) = Yield x (f y)---- | The 'Await' functor instance is equivalent to (->) but more descriptive.-data Await x y = Await! (x -> y)-instance Functor (Await x) where- fmap f (Await g) = Await (f . g)---- | The 'Naught' functor instance doesn't contain anything and cannot be constructed. Used for building non-suspendable--- coroutines.-data Naught x-instance Functor Naught where- fmap f _ = undefined---- | Combines two alternative functors into one, applying one or the other. Used for nested coroutines.-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)---- | Combines two functors into one, applying both.-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)---- | Combines two functors into one, applying either or both of them. Used for coupled coroutines.-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)---- | Suspend the current 'Coroutine'.-suspend :: (Monad m, Functor s) => s (Coroutine s m x) -> Coroutine s m x-suspend s = Coroutine (return (Suspend s))---- | Suspend yielding a value.-yield :: forall m x. Monad m => x -> Coroutine (Yield x) m ()-yield x = suspend (Yield x (return ()))---- | Suspend until a value is provided.-await :: forall m x. Monad m => Coroutine (Await x) m x-await = suspend (Await return)---- | Convert a non-suspending 'Coroutine' to the base monad.-runCoroutine :: Monad m => Coroutine Naught m x -> m x-runCoroutine = pogoStick (error "runCoroutine can run only a non-suspending coroutine!")---- | Run a 'Coroutine', using a function that converts suspension to the resumption it wraps.-pogoStick :: (Functor s, Monad m) => (s (Coroutine s m x) -> Coroutine s m x) -> Coroutine s m x -> m x-pogoStick reveal t = resume t- >>= \s-> case s - of Done result -> return result- Suspend c -> pogoStick reveal (reveal c)---- | Run a nested 'Coroutine' that can suspend both itself and the current 'Coroutine'.-pogoStickNested :: (Functor s1, Functor s2, Monad m) => - (s2 (Coroutine (EitherFunctor s1 s2) m x) -> Coroutine (EitherFunctor s1 s2) m x)- -> Coroutine (EitherFunctor s1 s2) m x -> Coroutine s1 m x-pogoStickNested reveal t = - Coroutine{resume= resume t- >>= \s-> case s- of Done result -> return (Done result)- Suspend (LeftF s) -> return (Suspend (fmap (pogoStickNested reveal) s))- Suspend (RightF c) -> resume (pogoStickNested reveal (reveal c))- }---- | Combines two values under two functors into a pair of values under a single 'NestedFunctor'.-nest :: (Functor a, Functor b) => a x -> b y -> NestedFunctor a b (x, y)-nest a b = NestedFunctor $ fmap (\x-> fmap ((,) x) b) a---- | Weaves two coroutines into one.-couple :: (Monad m, Functor s1, Functor s2) => - (forall x y r. (x -> y -> m r) -> m x -> m y -> m r)- -> Coroutine s1 m x -> Coroutine s2 m y -> Coroutine (SomeFunctor s1 s2) m (x, y)-couple runPair t1 t2 = Coroutine{resume= runPair proceed (resume t1) (resume t2)} where- proceed (Done x) (Done y) = return $ Done (x, y)- proceed (Suspend s1) (Suspend s2) = return $ Suspend $ fmap (uncurry (couple runPair)) (Both $ nest s1 s2)- proceed (Done x) (Suspend s2) = return $ Suspend $ fmap (couple runPair (return x)) (RightSome s2)- proceed (Suspend s1) (Done y) = return $ Suspend $ fmap (flip (couple runPair) (return y)) (LeftSome s1)---- | Weaves two nested coroutines into one.-coupleNested :: (Monad m, Functor s0, Functor s1, Functor s2) => - (forall x y r. (x -> y -> m r) -> m x -> m y -> m r)- -> Coroutine (EitherFunctor s0 s1) m x -> Coroutine (EitherFunctor s0 s2) m y- -> Coroutine (EitherFunctor s0 (SomeFunctor s1 s2)) m (x, y)-coupleNested runPair = coupleNested' where- coupleNested' t1 t2 = Coroutine{resume= runPair (\ st1 st2 -> return (proceed st1 st2)) (resume t1) (resume t2)}- proceed (Done x) (Done y) = Done (x, y)- proceed (Suspend (RightF s)) (Done y) = Suspend $ RightF $ fmap (flip coupleNested' (return y)) (LeftSome s)- proceed (Done x) (Suspend (RightF s)) = Suspend $ RightF $ fmap (coupleNested' (return x)) (RightSome s)- proceed (Suspend (RightF s1)) (Suspend (RightF s2)) =- Suspend $ RightF $ fmap (uncurry coupleNested') (Both $ nest s1 s2)- proceed (Suspend (LeftF s)) (Done y) = Suspend $ LeftF $ fmap (flip coupleNested' (return y)) s- proceed (Done x) (Suspend (LeftF s)) = Suspend $ LeftF $ fmap (coupleNested' (return x)) s- proceed (Suspend (LeftF s1)) (Suspend (LeftF s2)) = Suspend $ LeftF $ fmap (coupleNested' $ suspend $ LeftF s1) s2---- | A simple record containing the resolver functions for all possible coroutine pair suspensions.-data SeesawResolver s1 s2 = SeesawResolver {- resumeLeft :: forall t. s1 t -> t, -- ^ resolves the left suspension functor into the resumption it contains- resumeRight :: forall t. s2 t -> t, -- ^ resolves the right suspension into its resumption- -- | invoked when both coroutines are suspended, resolves both suspensions or either one- resumeAny :: forall t1 t2 r.- (t1 -> r) -- ^ continuation to resume only the left suspended coroutine- -> (t2 -> r) -- ^ continuation to resume the right coroutine only- -> (t1 -> t2 -> r) -- ^ continuation to resume both coroutines- -> s1 t1 -- ^ left suspension- -> s2 t2 -- ^ right suspension- -> r-}---- | Runs two coroutines concurrently. The first argument is used to run the next step of each coroutine, the next to--- convert the left, right, or both suspensions into the corresponding resumptions.-seesaw :: (Monad m, Functor s1, Functor s2) => - (forall x y r. (x -> y -> m r) -> m x -> m y -> m r)- -> SeesawResolver s1 s2- -> Coroutine s1 m x -> Coroutine s2 m y -> m (x, y)-seesaw runPair resolver t1 t2 = seesaw' t1 t2 where- seesaw' t1 t2 = runPair proceed (resume t1) (resume t2)- proceed (Done x) (Done y) = return (x, y)- proceed (Done x) (Suspend s2) = seesaw' (return x) (resumeRight resolver s2)- proceed (Suspend s1) (Done y) = seesaw' (resumeLeft resolver s1) (return y)- proceed (Suspend s1) (Suspend s2) =- resumeAny resolver (flip seesaw' (suspend s2)) (seesaw' (suspend s1)) seesaw' s1 s2---- | Like 'seesaw', but for nested coroutines that are allowed to suspend the current coroutine as well as themselves.-seesawNested :: (Monad m, Functor s0, Functor s1, Functor s2) =>- (forall x y r. (x -> y -> m r) -> m x -> m y -> m r)- -> SeesawResolver s1 s2- -> Coroutine (EitherFunctor s0 s1) m x -> Coroutine (EitherFunctor s0 s2) m y -> Coroutine s0 m (x, y)-seesawNested runPair resolver t1 t2 = seesaw' t1 t2 where- seesaw' t1 t2 = Coroutine{resume= bouncePair t1 t2}- bouncePair t1 t2 = runPair proceed (resume t1) (resume t2)- proceed (Suspend (LeftF s1)) state2 = return $ Suspend $ fmap ((flip seesaw' (Coroutine $ return state2))) s1- proceed state1 (Suspend (LeftF s2)) = return $ Suspend $ fmap (seesaw' (Coroutine $ return state1)) s2- proceed (Done x) (Done y) = return $ Done (x, y)- proceed state1@(Done x) (Suspend (RightF s2)) = proceed state1 =<< resume (resumeRight resolver s2)- proceed (Suspend (RightF s1)) state2@(Done y) = flip proceed state2 =<< resume (resumeLeft resolver s1)- proceed state1@(Suspend (RightF s1)) state2@(Suspend (RightF s2)) =- resumeAny resolver ((flip proceed state2 =<<) . resume) ((proceed state1 =<<) . resume) bouncePair s1 s2---- | Converts a coroutine into a nested one.-local :: forall m l r x. (Functor r, Monad m) => Coroutine r m x -> Coroutine (EitherFunctor l r) m x-local (Coroutine mr) = Coroutine (liftM inject mr)- where inject :: CoroutineState r m x -> CoroutineState (EitherFunctor l r) m x- inject (Done x) = Done x- inject (Suspend r) = Suspend (RightF $ fmap local r)---- | Converts a coroutine into one that can contain nested coroutines.-out :: forall m l r x. (Functor l, Monad m) => Coroutine l m x -> Coroutine (EitherFunctor l r) m x-out (Coroutine ml) = Coroutine (liftM inject ml)- where inject :: CoroutineState l m x -> CoroutineState (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)---- | Like 'out', working over multiple functors.-liftOut :: forall m a d x. (Monad m, Functor a, AncestorFunctor a d) => Coroutine a m x -> Coroutine d m x-liftOut (Coroutine ma) = Coroutine (liftM inject ma)- where inject :: CoroutineState a m x -> CoroutineState d m x- inject (Done x) = Done x- inject (Suspend a) = Suspend (liftFunctor $ fmap liftOut a)
Control/Concurrent/SCC/Combinators.hs view
@@ -1,1101 +1,947 @@ {- - Copyright 2008-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/>.--}--{-# LANGUAGE ScopedTypeVariables, Rank2Types, KindSignatures, EmptyDataDecls,- MultiParamTypeClasses, FlexibleContexts, FlexibleInstances, FunctionalDependencies, TypeFamilies #-}---- | The "Combinators" module defines combinators applicable to values of the 'Transducer' and 'Splitter' types defined--- in the "Control.Concurrent.SCC.Types" module.--module Control.Concurrent.SCC.Combinators- (-- * Consumer, producer, and transducer combinators- splitterToMarker,- consumeBy, prepend, append, substitute,- PipeableComponentPair (connect), JoinableComponentPair (join, sequence),- -- * Pseudo-logic splitter combinators- -- | Combinators '>&' and '>|' are only /pseudo/-logic. While the laws of double negation and De Morgan's laws hold,- -- '>&' and '>|' are in general not commutative, associative, nor idempotent. In the special case when all argument- -- splitters are stateless, such as those produced by 'Components.liftStatelessSplitter', these combinators do satisfy- -- all laws of Boolean algebra.- sNot, sAnd, sOr,- -- ** Zipping logic combinators- -- | The '&&' and '||' combinators run the argument splitters in parallel and combine their logical outputs using- -- the corresponding logical operation on each output pair, in a manner similar to 'Prelude.zipWith'. They fully- -- satisfy the laws of Boolean algebra.- pAnd, pOr,- -- * Flow-control combinators- -- | The following combinators resemble the common flow-control programming language constructs. Combinators - -- 'wherever', 'unless', and 'select' are just the special cases of the combinator 'ifs'.- --- -- * /transducer/ ``wherever`` /splitter/ = 'ifs' /splitter/ /transducer/ 'Components.asis'- --- -- * /transducer/ ``unless`` /splitter/ = 'ifs' /splitter/ 'Components.asis' /transducer/- --- -- * 'select' /splitter/ = 'ifs' /splitter/ 'Components.asis' 'Components.suppress'- --- ifs, wherever, unless, select,- -- ** Recursive- while, nestedIn,- -- * Section-based combinators- -- | All combinators in this section use their 'Splitter' argument to determine the structure of the input. Every- -- contiguous portion of the input that gets passed to one or the other sink of the splitter is treated as one- -- section in the logical structure of the input stream. What is done with the section depends on the combinator,- -- but the sections, and therefore the logical structure of the input stream, are determined by the argument- -- splitter alone.- foreach, having, havingOnly, followedBy, even,- -- ** first and its variants- first, uptoFirst, prefix,- -- ** last and its variants- last, lastAndAfter, suffix,- -- ** positional splitters- startOf, endOf,- -- ** input ranges- between,- -- * parser support- parseRegions, parseNestedRegions,- -- * grouping helpers- groupMarks)-where--import Control.Concurrent.Coroutine-import Control.Concurrent.SCC.Streams-import Control.Concurrent.SCC.Types--import Prelude hiding (even, last, sequence, (||), (&&))-import qualified Prelude-import Control.Exception (assert)-import Control.Monad (liftM, when)-import qualified Control.Monad as Monad-import Control.Monad.Trans (lift)-import Data.Maybe (isJust, isNothing, fromJust)-import qualified Data.Foldable as Foldable-import qualified Data.Sequence as Seq-import Data.Sequence (Seq, (|>), (><), ViewL (EmptyL, (:<)))--import Debug.Trace (trace)---- | Converts a 'Consumer' into a 'Transducer' with no output.-consumeBy :: forall m x y r. (Monad m) => Consumer m x r -> Transducer m x y-consumeBy c = Transducer $ \ source _sink -> consume c source >> return []---- | Class 'PipeableComponentPair' applies to any two components that can be combined into a third component with the--- following properties:------ * The input of the result, if any, becomes the input of the first component.------ * The output produced by the first child component is consumed by the second child component.------ * The result output, if any, is the output of the second component.-class PipeableComponentPair (m :: * -> *) w c1 c2 c3 | c1 c2 -> c3, c1 c3 -> c2, c2 c3 -> c2,- c1 -> m w, c2 -> m w, c3 -> m- where connect :: Bool -> c1 -> c2 -> c3--instance forall m x. (ParallelizableMonad m) =>- PipeableComponentPair m x (Producer m x ()) (Consumer m x ()) (Performer m ())- where connect parallel p c = let performPipe :: Coroutine Naught m ((), ())- performPipe = pipePS parallel (produce p) (consume c)- in Performer (runCoroutine performPipe >> return ())--instance (ParallelizableMonad m)- => PipeableComponentPair m y (Transducer m x y) (Consumer m y r) (Consumer m x r)- where connect parallel t c = isolateConsumer $ \source-> - liftM snd $- pipePS parallel- (transduce t source)- (consume c)--instance (ParallelizableMonad m) => PipeableComponentPair m x (Producer m x r) (Transducer m x y) (Producer m y r)- where connect parallel p t = isolateProducer $ \sink-> - liftM fst $- pipePS parallel- (produce p)- (\source-> transduce t source sink)--instance ParallelizableMonad m => PipeableComponentPair m y (Transducer m x y) (Transducer m y z) (Transducer m x z)- where connect parallel t1 t2 = isolateTransducer $ \source sink-> - liftM fst $- pipePS parallel- (transduce t1 source)- (\source-> transduce t2 source sink)--class CompatibleSignature c cons (m :: * -> *) input output | c -> cons m--class AnyListOrUnit c--instance AnyListOrUnit [x]-instance AnyListOrUnit ()--instance (AnyListOrUnit x, AnyListOrUnit y) => CompatibleSignature (Performer m r) (PerformerType r) m x y-instance AnyListOrUnit y => CompatibleSignature (Consumer m x r) (ConsumerType r) m [x] y-instance AnyListOrUnit y => CompatibleSignature (Producer m x r) (ProducerType r) m y [x]-instance CompatibleSignature (Transducer m x y) TransducerType m [x] [y]--data PerformerType r-data ConsumerType r-data ProducerType r-data TransducerType---- | Class 'JoinableComponentPair' applies to any two components that can be combined into a third component with the--- following properties:------ * if both argument components consume input, the input of the combined component gets distributed to both--- components in parallel,------ * if both argument components produce output, the output of the combined component is a concatenation of the--- complete output from the first component followed by the complete output of the second component, and------ * the 'join' method may apply the components in any order, the 'sequence' method makes sure its first argument--- has completed before using the second one.-class (Monad m, CompatibleSignature c1 t1 m x y, CompatibleSignature c2 t2 m x y, CompatibleSignature c3 t3 m x y)- => JoinableComponentPair t1 t2 t3 m x y c1 c2 c3 | c1 c2 -> c3, c1 -> t1 m, c2 -> t2 m, c3 -> t3 m x y,- t1 m x y -> c1, t2 m x y -> c2, t3 m x y -> c3- where join :: Bool -> c1 -> c2 -> c3- sequence :: c1 -> c2 -> c3- join = const sequence--instance forall m x r1 r2. Monad m =>- JoinableComponentPair (ProducerType r1) (ProducerType r2) (ProducerType r2) m () [x]- (Producer m x r1) (Producer m x r2) (Producer m x r2)- where sequence p1 p2 = Producer $ \sink-> produce p1 sink >> produce p2 sink--instance forall m x. ParallelizableMonad m =>- JoinableComponentPair (ConsumerType ()) (ConsumerType ()) (ConsumerType ()) m [x] ()- (Consumer m x ()) (Consumer m x ()) (Consumer m x ())- where join parallel c1 c2 = isolateConsumer $ \source->- pipePS parallel- (\sink1-> pipe (tee source sink1) (consume c2))- (consume c1)- >> return ()- sequence c1 c2 = isolateConsumer $ \source->- pipe- (\buffer-> pipe (tee source buffer) (consume c1))- getList- >>= \(_, list)-> pipe (putList list) (consume c2)- >> return ()--instance forall m x y. (ParallelizableMonad m) =>- JoinableComponentPair TransducerType TransducerType TransducerType m [x] [y]- (Transducer m x y) (Transducer m x y) (Transducer m x y)- where join parallel t1 t2 = isolateTransducer $ \source sink->- pipe- (\buffer-> pipePS parallel- (\sink1-> pipe- (\sink2-> tee source sink1 sink2)- (\src-> transduce t2 src buffer))- (\source-> transduce t1 source sink))- getList- >>= \(_, list)-> putList list sink- >> getList source- sequence t1 t2 = isolateTransducer $ \source sink->- pipe- (\buffer-> pipe- (tee source buffer)- (\source-> transduce t1 source sink))- getList- >>= \(_, list)-> pipe- (\sink-> putList list sink- >>= whenNull (pour source sink- >> return []))- (\source-> transduce t2 source sink)- >>= return . fst--instance forall m r1 r2. ParallelizableMonad m =>- JoinableComponentPair (PerformerType r1) (PerformerType r2) (PerformerType r2) m () ()- (Performer m r1) (Performer m r2) (Performer m r2)- where join parallel p1 p2 = Performer $ if parallel- then bindM2 (const return) (perform p1) (perform p2)- else perform p1 >> perform p2- sequence p1 p2 = Performer $ perform p1 >> perform p2--instance forall m x r1 r2. (ParallelizableMonad m) =>- JoinableComponentPair (PerformerType r1) (ProducerType r2) (ProducerType r2) m () [x]- (Performer m r1) (Producer m x r2) (Producer m x r2)- where join parallel pe pr = Producer $ \sink-> if parallel- then bindM2 (const return) (lift (perform pe)) (produce pr sink)- else lift (perform pe) >> produce pr sink- sequence pe pr = Producer $ \sink-> lift (perform pe) >> produce pr sink--instance forall m x r1 r2. (ParallelizableMonad m) =>- JoinableComponentPair (ProducerType r1) (PerformerType r2) (ProducerType r2) m () [x]- (Producer m x r1) (Performer m r2) (Producer m x r2)- where join parallel pr pe = Producer $ \sink-> if parallel- then bindM2 (const return) (produce pr sink) (lift (perform pe))- else produce pr sink >> lift (perform pe)- sequence pr pe = Producer $ \sink-> produce pr sink >> lift (perform pe)--instance forall m x r1 r2. (ParallelizableMonad m) =>- JoinableComponentPair (PerformerType r1) (ConsumerType r2) (ConsumerType r2) m [x] ()- (Performer m r1) (Consumer m x r2) (Consumer m x r2)- where join parallel p c = Consumer $ \source-> if parallel- then bindM2 (const return) (lift (perform p)) (consume c source)- else lift (perform p) >> consume c source- sequence p c = Consumer $ \source-> lift (perform p) >> consume c source--instance forall m x r1 r2. (ParallelizableMonad m) =>- JoinableComponentPair (ConsumerType r1) (PerformerType r2) (ConsumerType r2) m [x] ()- (Consumer m x r1) (Performer m r2) (Consumer m x r2)- where join parallel c p = Consumer $ \source-> if parallel- then bindM2 (const return) (consume c source) (lift (perform p))- else consume c source >> lift (perform p)- sequence c p = Consumer $ \source-> consume c source >> lift (perform p)--instance forall m x y r. (ParallelizableMonad m) =>- JoinableComponentPair (PerformerType r) TransducerType TransducerType m [x] [y]- (Performer m r) (Transducer m x y) (Transducer m x y)- where join parallel p t = Transducer $ \ source sink -> if parallel- then bindM2 (const return)- (lift (perform p)) (transduce t source sink)- else lift (perform p) >> transduce t source sink- sequence p t = Transducer $ \ source sink -> lift (perform p) >> transduce t source sink--instance forall m x y r. (ParallelizableMonad m)- => JoinableComponentPair TransducerType (PerformerType r) TransducerType m [x] [y]- (Transducer m x y) (Performer m r) (Transducer m x y)- where join parallel t p = Transducer $ \ source sink -> if parallel- then bindM2 (const . return)- (transduce t source sink) (lift (perform p))- else do result <- transduce t source sink- lift (perform p)- return result- sequence t p = Transducer $ \ source sink -> do result <- transduce t source sink- lift (perform p)- return result--instance forall m x y. (ParallelizableMonad m) =>- JoinableComponentPair (ProducerType ()) TransducerType TransducerType m [x] [y]- (Producer m y ()) (Transducer m x y) (Transducer m x y)- where join parallel p t = if parallel- then isolateTransducer $ \source sink->- do (rest, out) <- pipe- (\buffer-> bindM2 (const return)- (produce p sink) (transduce t source buffer))- getList- putList out sink- return rest- else sequence p t- sequence p t = Transducer $ \ source sink -> produce p sink >> transduce t source sink--instance forall m x y. (ParallelizableMonad m) =>- JoinableComponentPair TransducerType (ProducerType ()) TransducerType m [x] [y]- (Transducer m x y) (Producer m y ()) (Transducer m x y)- where join parallel t p = if parallel- then isolateTransducer $ \source sink->- do (rest, out) <- pipe- (\buffer-> bindM2 (const . return)- (transduce t source sink)- (produce p buffer))- getList- putList out sink- return rest - else sequence t p- sequence t p = Transducer $ \ source sink -> do result <- transduce t source sink- produce p sink- return result--instance forall m x y. (ParallelizableMonad m) =>- JoinableComponentPair (ConsumerType ()) TransducerType TransducerType m [x] [y]- (Consumer m x ()) (Transducer m x y) (Transducer m x y)- where join parallel c t = isolateTransducer $ \source sink->- liftM (snd . fst) $- pipePS parallel- (\sink1-> pipe- (tee source sink1)- (\source-> transduce t source sink))- (consume c)- sequence c t = isolateTransducer $ \source sink->- pipe- (\buffer-> pipe- (tee source buffer)- (consume c))- getList- >>= \(_, list)-> pipe- (\sink-> putList list sink- >>= whenNull (pour source sink >> return []))- (\source-> transduce t source sink)- >>= return . fst--instance forall m x y. ParallelizableMonad m =>- JoinableComponentPair TransducerType (ConsumerType ()) TransducerType m [x] [y]- (Transducer m x y) (Consumer m x ()) (Transducer m x y)- where join parallel t c = join parallel c t- sequence t c = isolateTransducer $ \source sink->- pipe- (\buffer-> pipe- (tee source buffer)- (\source-> transduce t source sink))- getList- >>= \(_, list)-> pipe- (\sink-> putList list sink- >>= whenNull (pour source sink- >> return []))- (consume c)- >>= return . fst--instance forall m x y. (ParallelizableMonad m) =>- JoinableComponentPair (ProducerType ()) (ConsumerType ()) TransducerType m [x] [y]- (Producer m y ()) (Consumer m x ()) (Transducer m x y)- where join parallel p c = Transducer $ \ source sink ->- if parallel- then bindM2 (\ _ _ -> return []) (produce p sink) (consume c source)- else produce p sink >> consume c source >> return []- sequence p c = Transducer $ \ source sink -> produce p sink >> consume c source >> return []--instance forall m x y. (ParallelizableMonad m) =>- JoinableComponentPair (ConsumerType ()) (ProducerType ()) TransducerType m [x] [y]- (Consumer m x ()) (Producer m y ()) (Transducer m x y)- where join parallel c p = join parallel p c- sequence c p = Transducer $ \ source sink -> consume c source >> produce p sink >> return []---- | Combinator 'prepend' converts the given producer to transducer that passes all its input through unmodified, except--- | for prepending the output of the argument producer to it.--- | 'prepend' /prefix/ = 'join' ('substitute' /prefix/) 'asis'-prepend :: forall m x r. (Monad m) => Producer m x r -> Transducer m x x-prepend prefix = Transducer $ \ source sink -> produce prefix sink >> pour source sink >> return []---- | Combinator 'append' converts the given producer to transducer that passes all its input through unmodified, finally--- | appending to it the output of the argument producer.--- | 'append' /suffix/ = 'join' 'asis' ('substitute' /suffix/)-append :: forall m x r. (Monad m) => Producer m x r -> Transducer m x x-append suffix = Transducer $ \ source sink -> pour source sink >> produce suffix sink >> return []---- | The 'substitute' combinator converts its argument producer to a transducer that produces the same output, while--- | consuming its entire input and ignoring it.-substitute :: forall m x y r. (Monad m) => Producer m y r -> Transducer m x y-substitute feed = Transducer $ \ source sink -> consumeAndSuppress source >> produce feed sink >> return []---- | The 'snot' (streaming not) combinator simply reverses the outputs of the argument splitter. In other words, data--- that the argument splitter sends to its /true/ sink goes to the /false/ sink of the result, and vice versa.-sNot :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-sNot splitter = isolateSplitter $ \ source true false edge -> suppressProducer (split splitter source false true)---- | The '>&' combinator sends the /true/ sink output of its left operand to the input of its right operand for further--- splitting. Both operands' /false/ sinks are connected to the /false/ sink of the combined splitter, but any input--- value to reach the /true/ sink of the combined component data must be deemed true by both splitters.-sAnd :: forall m x b1 b2. ParallelizableMonad m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)-sAnd parallel s1 s2 =- isolateSplitter $ \ source true false edge ->- liftM (fst . fst . fst . fst) $- pipe- (\edges-> pipe- (\edge1-> pipe- (\edge2-> pipePS parallel- (\true-> split s1 source true false edge1)- (\source-> split s2 source true false edge2))- (flip (pourMap Right) edges))- (flip (pourMap Left) edges))- (flip intersectRegions edge)--intersectRegions source sink = next Nothing Nothing- where next lastLeft lastRight = get source- >>= maybe- (return ())- (either- (flip pair lastRight . Just)- (pair lastLeft . Just))- pair l@(Just x) r@(Just y) = put sink (x, y)- >>= flip when (next Nothing Nothing)- pair l r = next l r---- | A '>|' combinator's input value can reach its /false/ sink only by going through both argument splitters' /false/--- sinks.-sOr :: forall m x b1 b2. ParallelizableMonad m =>- Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)-sOr parallel s1 s2 = isolateSplitter $ \ source true false edge ->- liftM (fst . fst . fst) $- pipe- (\edge1-> pipe- (\edge2-> pipePS parallel- (\false-> split s1 source true false edge1)- (\source-> split s2 source true false edge2))- (flip (pourMap Right) edge))- (flip (pourMap Left) edge)---- | Combinator '&&' is a pairwise logical conjunction of two splitters run in parallel on the same input.-pAnd :: forall m x b1 b2. ParallelizableMonad m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)-pAnd parallel s1 s2 = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipePS parallel- (transduce (splittersToPairMarker parallel s1 s2) source)- (\source-> let split l r = get source- >>= maybe- (return [])- (test l r)- test l r (Left (x, t1, t2))- = (if t1 Prelude.&& t2 then put true x else put false x)- >>= cond- (split- (if t1 then l else Nothing)- (if t2 then r else Nothing))- (return [x])- test _ Nothing (Right (Left l)) = split (Just l) Nothing- test _ (Just r) (Right (Left l))- = put edge (l, r) >> split (Just l) (Just r)- test Nothing _ (Right (Right r)) = split Nothing (Just r)- test (Just l) _ (Right (Right r))- = put edge (l, r) >> split (Just l) (Just r)- in split Nothing Nothing)---- | Combinator '||' is a pairwise logical disjunction of two splitters run in parallel on the same input.-pOr :: forall c m x b1 b2. ParallelizableMonad m =>- Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)-pOr = zipSplittersWith (Prelude.||) pour--ifs :: forall c m x b. (ParallelizableMonad m, Branching c m x [x]) => Bool -> Splitter m x b -> c -> c -> c-ifs parallel s c1 c2 = combineBranches if' parallel c1 c2- where if' :: forall d. Bool -> (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x [x]) ->- (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x [x]) ->- forall a. OpenConsumer m a d x [x]- if' parallel c1 c2 source = splitInputToConsumers parallel s source c1 c2--wherever :: forall m x b. ParallelizableMonad m => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x-wherever parallel t s = isolateTransducer $ \source sink->- splitInputToConsumers parallel s source- (\source-> transduce t source sink)- (\source-> pour source sink >> return [])--unless :: forall m x b. ParallelizableMonad m => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x-unless parallel t s = isolateTransducer $ \source sink->- splitInputToConsumers parallel s source- (\source-> pour source sink >> return [])- (\source-> transduce t source sink)--select :: forall m x b. Monad m => Splitter m x b -> Transducer m x x-select s = isolateTransducer $ \source sink-> suppressProducer (suppressProducer . split s source sink)---- | Converts a splitter into a parser.-parseRegions :: forall m x b. Monad m => Splitter m x b -> Parser m x b-parseRegions s = isolateTransducer $ \source sink->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker s) source)- (\source-> wrapRegions source sink)- where wrapRegions source sink = let wrap0 mb = get source- >>= maybe- (maybe (return True) flush mb >> return [])- (wrap1 mb)- wrap1 Nothing (Left (x, _)) = put sink (Content x)- >>= cond (wrap0 Nothing) (return [x])- wrap1 (Just p) (Left (x, False)) = flush p- >> put sink (Content x)- >>= cond- (wrap0 Nothing)- (return [x])- wrap1 (Just (b, t)) (Left (x, True))- = (if t then return True else put sink (Markup (Start b)))- >> put sink (Content x)- >>= cond (wrap0 (Just (b, True))) (return [x])- wrap1 (Just p) (Right b') = flush p >> wrap0 (Just (b', False))- wrap1 Nothing (Right b) = wrap0 (Just (b, False))- flush (b, t) = put sink $ Markup $ (if t then End else Point) b- in wrap0 Nothing---- | Converts a boundary-marking splitter into a parser.-parseNestedRegions :: forall m x b. Monad m => Splitter m x (Boundary b) -> Parser m x b-parseNestedRegions s = isolateTransducer $ \source sink->- liftM (\(w, (), (), _)-> w) $- splitToConsumers s source- (flip (pourMap Content) sink)- (flip (pourMap Content) sink)- (flip (pourMap Markup) sink)---- | The recursive combinator 'while' feeds the true sink of the argument splitter back to itself, modified by the--- argument transducer. Data fed to the splitter's false sink is passed on unmodified.-while :: forall m x b. ParallelizableMonad m => [(Bool, (Transducer m x x, Splitter m x b))] -> Transducer m x x-while ((parallel, (t, s)) : rest) =- isolateTransducer $ \source sink->- splitInputToConsumers parallel s source- (\source-> get source- >>= maybe- (return [])- (\x-> liftM (uncurry (++)) $- pipe- (\sink-> put sink x >>= cond (pour source sink >> return []) (return [x]))- (\source-> transduce while' source sink)))- (\source-> pour source sink >> return [])- where while' = connect parallel t (while rest)---- | The recursive combinator 'nestedIn' combines two splitters into a mutually recursive loop acting as a single--- splitter. The true sink of one of the argument splitters and false sink of the other become the true and false sinks--- of the loop. The other two sinks are bound to the other splitter's source. The use of 'nestedIn' makes sense only--- on hierarchically structured streams. If we gave it some input containing a flat sequence of values, and assuming--- both component splitters are deterministic and stateless, an input value would either not loop at all or it would--- loop forever.-nestedIn :: forall m x b. ParallelizableMonad m => [(Bool, (Splitter m x b, Splitter m x b))] -> Splitter m x b-nestedIn ((parallel, (s1, s2)) : rest) =- isolateSplitter $ \ source true false edge ->- liftM fst $- pipePS parallel- (\false-> split s1 source true false edge)- (\source-> pipe- (\true-> pipe (split s2 source true false) consumeAndSuppress)- (\source-> get source- >>= maybe- (return ([], []))- (\x-> pipe- (\sink-> put sink x- >>= cond- (pour source sink >> return [])- (return [x]))- (\source-> split (nestedIn rest) source true false edge))))---- | The 'foreach' combinator is similar to the combinator 'ifs' in that it combines a splitter and two transducers into--- another transducer. However, in this case the transducers are re-instantiated for each consecutive portion of the--- input as the splitter chunks it up. Each contiguous portion of the input that the splitter sends to one of its two--- sinks gets transducered through the appropriate argument transducer as that transducer's whole input. As soon as the--- contiguous portion is finished, the transducer gets terminated.-foreach :: forall m x b c. (ParallelizableMonad m, Branching c m x [x]) => Bool -> Splitter m x b -> c -> c -> c-foreach parallel s c1 c2 = combineBranches foreach' parallel c1 c2- where foreach' :: forall d. Bool -> - (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x [x]) ->- (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x [x]) ->- forall a. OpenConsumer m a d x [x]- foreach' parallel c1 c2 source =- liftM fst $- pipePS parallel- (transduce (splitterToMarker s) (liftSource source :: Source m d x))- (\source-> groupMarks source (maybe c2 (const c1)))---- | The 'having' combinator combines two pure splitters into a pure splitter. One splitter is used to chunk the input--- into contiguous portions. Its /false/ sink is routed directly to the /false/ sink of the combined splitter. The--- second splitter is instantiated and run on each portion of the input that goes to first splitter's /true/ sink. If--- the second splitter sends any output at all to its /true/ sink, the whole input portion is passed on to the /true/--- sink of the combined splitter, otherwise it goes to its /false/ sink.-having :: forall m x b1 b2. ParallelizableMonad m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1-having parallel s1 s2 = isolateSplitter s- where s source true false edge = liftM fst $- pipePS parallel- (transduce (splitterToMarker s1) source)- (flip groupMarks test)- where test Nothing chunk = pour chunk false >> return []- test (Just mb) chunk = pipe- (\sink1-> pipe (tee chunk sink1) getList)- (\chunk-> splitToConsumers s2 chunk- (liftM isJust . get)- consumeAndSuppress- (liftM isJust . get))- >>= \(((), prefix), (_, anyTrue, (), anyEdge))->- if anyTrue Prelude.|| anyEdge- then maybe (return True) (put edge) mb- >> putList prefix true- >>= whenNull (pour chunk true >> return [])- else putList prefix false- >>= whenNull (pour chunk false >> return [])---- | The 'havingOnly' combinator is analogous to the 'having' combinator, but it succeeds and passes each chunk of the--- input to its /true/ sink only if the second splitter sends no part of it to its /false/ sink.-havingOnly :: forall m x b1 b2. ParallelizableMonad m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1-havingOnly parallel s1 s2 = isolateSplitter s- where s source true false edge = liftM fst $- pipePS parallel- (transduce (splitterToMarker s1) source)- (flip groupMarks test)- where test Nothing chunk = pour chunk false >> return []- test (Just mb) chunk = pipe- (\sink1-> pipe (tee chunk sink1) getList)- (\chunk-> splitToConsumers s2 chunk- consumeAndSuppress- (liftM isJust . get)- consumeAndSuppress)- >>= \(((), prefix), (_, (), anyFalse, ()))->- if anyFalse- then putList prefix false- >>= whenNull (pour chunk false >> return [])- else maybe (return True) (put edge) mb- >> putList prefix true- >>= whenNull (pour chunk true >> return [])---- | The result of combinator 'first' behaves the same as the argument splitter up to and including the first portion of--- the input which goes into the argument's /true/ sink. All input following the first true portion goes into the--- /false/ sink.-first :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-first splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = pass false x get1- get1 (Left (x, True)) = pass true x get2- get1 (Right b) = put edge b- >> get source- >>= maybe (return []) get2- get2 b@Right{} = get3 b- get2 (Left (x, True)) = pass true x get2- get2 (Left (x, False)) = pass false x get3- get3 (Left (x, _)) = pass false x get3- get3 (Right _) = get source >>= maybe (return []) get3- pass sink x next = put sink x- >>= cond- (get source- >>= maybe (return []) next)- (return [x])- in get source >>= maybe (return []) get1)---- | The result of combinator 'uptoFirst' takes all input up to and including the first portion of the input which goes--- into the argument's /true/ sink and feeds it to the result splitter's /true/ sink. All the rest of the input goes--- into the /false/ sink. The only difference between 'first' and 'uptoFirst' combinators is in where they direct the--- /false/ portion of the input preceding the first /true/ part.-uptoFirst :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-uptoFirst splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 q (Left (x, False)) = let q' = q |> x- in get source- >>= maybe- (putQueue q' false)- (get1 q')- get1 q p@(Left (_, True)) = putQueue q true- >>= whenNull (get2 p)- get1 q (Right b) = putQueue q true- >>= whenNull (put edge b- >> get source- >>= maybe (return []) get2)- get2 b@Right{} = get3 b- get2 (Left (x, True)) = pass true x get2- get2 (Left (x, False)) = pass false x get3- get3 (Left (x, _)) = pass false x get3- get3 (Right _) = get source >>= maybe (return []) get3- pass sink x next = put sink x- >>= cond- (get source- >>= maybe (return []) next)- (return [x])- in get source >>= maybe (return []) (get1 Seq.empty))---- | The result of the combinator 'last' is a splitter which directs all input to its /false/ sink, up to the last--- portion of the input which goes to its argument's /true/ sink. That portion of the input is the only one that goes to--- the resulting component's /true/ sink. The splitter returned by the combinator 'last' has to buffer the previous two--- portions of its input, because it cannot know if a true portion of the input is the last one until it sees the end of--- the input or another portion succeeding the previous one.-last :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-last splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = put false x- >>= cond (get source- >>= maybe (return []) get1)- (return [x])- get1 p@(Left (x, True)) = get2 Nothing Seq.empty p- get1 (Right b) = pass (get2 (Just b) Seq.empty)- get2 mb q (Left (x, True)) = let q' = q |> x- in get source- >>= maybe- (flush mb q')- (get2 mb q')- get2 mb q p = get3 mb q Seq.empty p- get3 mb qt qf (Left (x, False)) =- let qf' = qf |> x- in get source- >>= maybe- (flush mb qt >> putQueue qf' false)- (get3 mb qt qf')- get3 mb qt qf p = do rest1 <- putQueue qt false- rest2 <- putQueue qf false- if null rest1 Prelude.&& null rest2- then get1 p- else return (rest1 ++ rest2)- flush mb q = maybe (return True) (put edge) mb- >> putQueue q true- pass succeed = get source >>= maybe (return []) succeed- in pass get1)---- | The result of the combinator 'lastAndAfter' is a splitter which directs all input to its /false/ sink, up to the--- last portion of the input which goes to its argument's /true/ sink. That portion and the remainder of the input is--- fed to the resulting component's /true/ sink. The difference between 'last' and 'lastAndAfter' combinators is where--- they feed the /false/ portion of the input, if any, remaining after the last /true/ part.-lastAndAfter :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-lastAndAfter splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = put false x- >>= cond- (pass get1)- (return [x])- get1 p@(Left (x, True)) = get2 Nothing Seq.empty p- get1 (Right b) = pass (get2 (Just b) Seq.empty)- get2 mb q (Left (x, True)) = let q' = q |> x- in get source- >>= maybe- (flush mb q')- (get2 mb q')- get2 mb q p = get3 mb q p- get3 mb q (Left (x, False)) = let q' = q |> x- in get source- >>= maybe- (flush mb q')- (get3 mb q')- get3 _ q p@(Left (x, True)) = putQueue q false- >>= whenNull (get1 p)- get3 _ q b'@Right{} = putQueue q false- >>= whenNull (get1 b')- flush mb q = maybe (return True) (put edge) mb- >> putQueue q true- pass succeed = get source >>= maybe (return []) succeed- in pass get1)---- | The 'prefix' combinator feeds its /true/ sink only the prefix of the input that its argument feeds to its /true/--- sink. All the rest of the input is dumped into the /false/ sink of the result.-prefix :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-prefix splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get0 p@Left{} = get1 p- get0 (Right b) = put edge b- >> get source- >>= maybe (return []) get1- get1 (Left (x, False)) = pass false x get2- get1 (Left (x, True)) = pass true x get1- get1 (Right b) = get source >>= maybe (return []) get2- get2 (Left (x, _)) = pass false x get2- get2 Right{} = get source >>= maybe (return []) get2- pass sink x next = put sink x- >>= cond- (get source- >>= maybe (return []) next)- (return [x])- in get source >>= maybe (return []) get0)---- | The 'suffix' combinator feeds its /true/ sink only the suffix of the input that its argument feeds to its /true/--- sink. All the rest of the input is dumped into the /false/ sink of the result.-suffix :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-suffix splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = put false x- >>= cond (p get1) (return [x])- get1 (Left (x, True)) = get2 Nothing (Seq.singleton x)- get1 (Right b) = get2 (Just b) Seq.empty- get2 mb q = get source- >>= maybe- (maybe (return True) (put edge) mb- >> putQueue q true)- (get3 mb q)- get3 mb q (Left (x, True)) = get2 mb (q |> x)- get3 mb q p@(Left (x, False)) =- putQueue q false- >>= \rest-> if null rest- then get1 p- else return (rest ++ [x])- get3 mb q (Right b) = putQueue q false- >>= whenNull (get2 (Just b) Seq.empty)- p succeed = get source >>= maybe (return []) succeed- in p get1)---- | The 'even' combinator takes every input section that its argument /splitter/ deems /true/, and feeds even ones into--- its /true/ sink. The odd sections and parts of input that are /false/ according to its argument splitter are fed to--- 'even' splitter's /false/ sink.-even :: forall m x b. Monad m => Splitter m x b -> Splitter m x b-even splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = put false x- >>= cond (next get1) (return [x])- get1 p@(Left (x, True)) = get2 p- get1 (Right b) = next get2- get2 (Left (x, True)) = put false x- >>= cond (next get2) (return [x])- get2 p@(Left (x, False)) = get3 p- get2 (Right b) = put edge b >> next get4- get3 (Left (x, False)) = put false x- >>= cond (next get3) (return [x])- get3 p@(Left (x, True)) = get4 p- get3 (Right b) = put edge b >> next get4- get4 (Left (x, True)) = put true x- >>= cond (next get4) (return [x])- get4 p@(Left (x, False)) = get1 p- get4 (Right b) = next get2- next g = get source >>= maybe (return []) g- in next get1)---- | Splitter 'startOf' issues an empty /true/ section at the beginning of every section considered /true/ by its--- argument splitter, otherwise the entire input goes into its /false/ sink.-startOf :: forall m x b. Monad m => Splitter m x b -> Splitter m x (Maybe b)-startOf splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = put false x- >>= cond- (next get1)- (return [x])- get1 p@(Left (x, True)) = put edge Nothing >> get2 p- get1 (Right b) = put edge (Just b)- >> next get2- get2 (Left (x, True)) = put false x- >>= cond- (next get2)- (return [x])- get2 p = get1 p- next g = get source >>= maybe (return []) g- in next get1)---- | Splitter 'endOf' issues an empty /true/ section at the end of every section considered /true/ by its argument--- splitter, otherwise the entire input goes into its /false/ sink.-endOf :: forall m x b. Monad m => Splitter m x b -> Splitter m x (Maybe b)-endOf splitter = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipe- (transduce (splitterToMarker splitter) source)- (\source-> let get1 (Left (x, False)) = put false x- >>= cond- (next get1)- (return [x])- get1 p@(Left (x, True)) = get2 Nothing p- get1 (Right b) = next (get2 $ Just b)- get2 mb (Left (x, True))- = put false x- >>= cond (next $ get2 mb) (return [x])- get2 mb p@(Left (x, False)) = put edge mb >> get1 p- get2 mb (Right b) = put edge mb >> next (get2 $ Just b)- next g = get source >>= maybe (return []) g- in next get1)---- | Combinator 'followedBy' treats its argument 'Splitter's as patterns components and returns a 'Splitter' that--- matches their concatenation. A section of input is considered /true/ by the result iff its prefix is considered--- /true/ by argument /s1/ and the rest of the section is considered /true/ by /s2/. The splitter /s2/ is started anew--- after every section split to /true/ sink by /s1/.-followedBy :: forall m x b1 b2. ParallelizableMonad m =>- Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)-followedBy parallel s1 s2 = - isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipePS parallel- (transduce (splitterToMarker s1) source)- (\source-> let get0 q = case Seq.viewl q- of Seq.EmptyL -> get source >>= maybe (return []) get1- (Left (x, False)) :< rest -> put false x- >>= cond- (get0 rest)- (return- $ concatMap (either ((:[]) . fst) (const []))- $ Foldable.toList $ Seq.viewl q)- (Left (x, True)) :< rest -> get2 Nothing Seq.empty q- (Right b) :< rest -> get2 (Just b) Seq.empty rest- get1 (Left (x, False)) = put false x- >>= cond (get source >>= maybe (return []) get1)- (return [x])- get1 p@(Left (x, True)) = get2 Nothing Seq.empty (Seq.singleton p)- get1 (Right b) = get2 (Just b) Seq.empty Seq.empty- get2 mb q q' = case Seq.viewl q'- of Seq.EmptyL -> get source- >>= maybe (testEnd mb q) (get2 mb q . Seq.singleton)- (Left (x, True)) :< rest -> get2 mb (q |> x) rest- (Left (x, False)) :< rest -> get3 mb q q'- Right{} :< rest -> get3 mb q q'- get3 mb q q' = do ((q1, q2), n) <- pipe (get7 Seq.empty q') (test mb q)- case n of Nothing -> putQueue q false- >>= whenNull (get0 (q1 >< q2))- Just 0 -> get0 (q1 >< q2)- Just n -> get8 (Just mb) n (q1 >< q2)- get7 q1 q2 sink = canPut sink- >>= cond (case Seq.viewl q2- of Seq.EmptyL -> get source- >>= maybe (return (q1, q2))- (\p-> either- (put sink . fst)- (const $ return True)- p- >> get7 (q1 |> p) q2 sink)- p :< rest -> either- (put sink . fst)- (const $ return True) p- >> get7 (q1 |> p) rest sink)- (return (q1, q2))- testEnd mb q = do ((), n) <- pipe (const $ return ()) (test mb q)- case n of Nothing -> putQueue q false- _ -> return []- test mb q source = liftM snd $- pipe- (transduce (splitterToMarker s2) source)- (\source-> let get4 (Left (_, False)) = return Nothing- get4 p@(Left (_, True)) = putQueue q true- >> get5 0 p- get4 p@(Right b) = maybe- (return True)- (\b1-> put edge (b1, b)) mb- >> putQueue q true- >> get6 0- get5 n (Left (x, True)) = put true x- >> get6 (succ n)- get5 n _ = return (Just n)- get6 n = get source- >>= maybe- (return $ Just n)- (get5 n)- in get source >>= maybe (return Nothing) get4)- get8 Nothing 0 q = get0 q- get8 (Just mb) 0 q = get2 mb Seq.empty q- get8 mmb n q = case Seq.viewl q of Left (x, False) :< rest -> get8 Nothing (pred n) rest- Left (x, True) :< rest- -> get8 (maybe (Just Nothing) Just mmb) (pred n) rest- Right b :< rest -> get8 (Just (Just b)) n rest- in get0 Seq.empty)---- | Combinator '...' tracks the running balance of difference between the number of preceding starts of sections--- considered /true/ according to its first argument and the ones according to its second argument. The combinator--- passes to /true/ all input values for which the difference balance is positive. This combinator is typically used--- with 'startOf' and 'endOf' in order to count entire input sections and ignore their lengths.-between :: forall m x b1 b2. ParallelizableMonad m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1-between parallel s1 s2 = isolateSplitter $ \ source true false edge ->- liftM (\(x, y)-> y ++ x) $- pipePS parallel- (transduce (splittersToPairMarker parallel s1 s2) source)- (\source-> let next n = get source >>= maybe (return []) (state n)- pass n x = (if n > 0 then put true x else put false x)- >>= cond (next n) (return [x])- pass' n x = (if n >= 0 then put true x else put false x)- >>= cond (next n) (return [x])- state n (Left (x, True, False)) = pass (succ n) x- state n (Left (x, False, True)) = pass' (pred n) x- state n (Left (x, True, True)) = pass' n x- state n (Left (x, False, False)) = pass n x- state 0 (Right (Left b)) = put edge b >> next 1- state n (Right (Left _)) = next (succ n)- state n (Right (Right _)) = next (pred n)- in next 0)---- Helper functions--splitterToMarker :: forall m x b. Monad m => Splitter m x b -> Transducer m x (Either (x, Bool) b)-splitterToMarker s = isolateTransducer $ \source sink->- let mark f source = canPut sink- >>= cond- (get source- >>= maybe (return [])- (\x-> put sink (f x)- >>= cond- (mark f source)- (return [x])))- (return [])- in liftM (\(x, y, z, _)-> z ++ y ++ x) $- splitToConsumers s source- (mark (\x-> Left (x, True)))- (mark (\x-> Left (x, False)))- (mark Right)--splittersToPairMarker :: forall m x b1 b2. (ParallelizableMonad m) => Bool -> Splitter m x b1 -> Splitter m x b2 ->- Transducer m x (Either (x, Bool, Bool) (Either b1 b2))-splittersToPairMarker parallel s1 s2 =- let t source sink = - liftM (\(((_, _), (x, _, _, _)), _)-> x) $- pipe- (\sync-> pipePS parallel- (\sink1-> pipe- (tee source sink1)- (\source2-> splitToConsumers s2 source2- (flip (pourMap (\x-> Left ((x, True), False))) sync)- (flip (pourMap (\x-> Left ((x, False), False))) sync)- (flip (pourMap (Right . Right)) sync)))- (\source1-> splitToConsumers s1 source1- (flip (pourMap (\x-> Left ((x, True), True))) sync)- (flip (pourMap (\x-> Left ((x, False), True))) sync)- (flip (pourMap (Right. Left)) sync)))- (synchronizeMarks Nothing sink)- -- synchronizeMarks :: Maybe (Seq (Either (x, Bool) (Either b1 b2)), Bool)- -- -> Sink m c (Either (x, Bool, Bool) (Either b1 b2))- -- -> Source m c (Either ((x, Bool), Bool) (Either b1 b2))- -- -> Coroutine c m [x]- synchronizeMarks state sink source = get source- >>= maybe- (assert (isNothing state) (return []))- (handleMark state sink source)- -- handleMark :: Maybe (Seq (Either (x, Bool) (Either b1 b2)), Bool)- -- -> Sink m c (Either (x, Bool, Bool) (Either b1 b2))- -- -> Source m c (Either ((x, Bool), Bool) (Either b1 b2))- -- -> Either ((x, Bool), Bool) (Either b1 b2) -> Coroutine c m [x]- handleMark Nothing sink source (Right b) = put sink (Right b)- >> synchronizeMarks Nothing sink source- handleMark Nothing sink source (Left (p, first))- = synchronizeMarks (Just (Seq.singleton (Left p), first)) sink source- handleMark state@(Just (q, first)) sink source (Left (p, first')) | first == first'- = synchronizeMarks (Just (q |> Left p, first)) sink source- handleMark state@(Just (q, True)) sink source (Right b@Left{})- = synchronizeMarks (Just (q |> Right b, True)) sink source- handleMark state@(Just (q, False)) sink source (Right b@Right{})- = synchronizeMarks (Just (q |> Right b, False)) sink source- handleMark state sink source (Right b) = put sink (Right b) >> synchronizeMarks state sink source- handleMark state@(Just (q, pos')) sink source mark@(Left ((x, t), pos))- = case Seq.viewl q- of Seq.EmptyL -> synchronizeMarks (Just (Seq.singleton (Left (x, t)), pos)) sink source- Right b :< rest -> put sink (Right b)- >>= cond- (handleMark- (if Seq.null rest then Nothing else Just (rest, pos'))- sink- source- mark)- (returnQueuedList q)- Left (y, t') :< rest -> put sink (Left $ if pos then (y, t, t') else (y, t', t))- >>= cond- (synchronizeMarks- (if Seq.null rest then Nothing else Just (rest, pos'))- sink- source)- (returnQueuedList q)- returnQueuedList q = return $ concatMap (either ((:[]) . fst) (const [])) $ Foldable.toList $ Seq.viewl q- in isolateTransducer t--zipSplittersWith :: forall m x b1 b2 b. ParallelizableMonad m => - (Bool -> Bool -> Bool) -> - (forall a1 a2 d. (AncestorFunctor a1 d, AncestorFunctor a2 d) =>- Source m a1 (Either b1 b2) -> Sink m a2 b -> Coroutine d m ()) -> - Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b-zipSplittersWith f boundaries parallel s1 s2- = isolateSplitter $ \ source true false edge ->- liftM (\((x, y), _)-> y ++ x) $- pipe- (\edge->- pipePS parallel- (transduce (splittersToPairMarker parallel s1 s2) source)- (\source-> let split = get source- >>= maybe- (return [])- (either- test- (\b-> put edge b >> split))- test (x, t1, t2) = (if f t1 t2 then put true x else put false x)- >>= cond split (return [x])- in split))- (flip boundaries edge)---- | Runs the second argument on every contiguous region of input source (typically produced by 'splitterToMarker')--- whose all values either match @Left (_, True)@ or @Left (_, False)@.-groupMarks :: (Monad m, AncestorFunctor a d, AncestorFunctor a (SinkFunctor d x)) =>- Source m a (Either (x, Bool) b) ->- (Maybe (Maybe b) -> Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m r) ->- Coroutine d m ()-groupMarks source getConsumer = start- where start = getSuccess source (either startContent startRegion)- startContent (x, False) = pipe (\sink-> pass False sink x) (getConsumer Nothing)- >>= maybe (return ()) (either startContent startRegion) . fst- startContent (x, True) = pipe (\sink-> pass True sink x) (getConsumer $ Just Nothing)- >>= maybe (return ()) (either startContent startRegion) . fst- startRegion b = pipe (next True) (getConsumer (Just $ Just b))- >>= maybe (return ()) (either startContent startRegion) . fst- pass t sink x = put sink x >> next t sink- next t sink = get source >>= maybe (return Nothing) (continue t sink)- continue t sink (Left (x, t')) | t == t' = pass t sink x- continue t sink p = return (Just p)---- | 'suppressProducer' runs the /producer/ argument with a new sink, suppressing everything 'put' in the sink.-suppressProducer :: forall m a x r. (Functor a, Monad m) => - (Sink m (SinkFunctor a x) x -> Coroutine (SinkFunctor a x) m r) -> Coroutine a m r-suppressProducer producer = liftM fst $ pipe producer consumeAndSuppress-+ Copyright 2008-2010 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/>.+-}++{-# LANGUAGE ScopedTypeVariables, Rank2Types, KindSignatures, EmptyDataDecls,+ MultiParamTypeClasses, FlexibleContexts, FlexibleInstances, FunctionalDependencies, TypeFamilies #-}++-- | The "Combinators" module defines combinators applicable to values of the 'Transducer' and 'Splitter' types defined+-- in the "Control.Concurrent.SCC.Types" module.++module Control.Concurrent.SCC.Combinators+ (-- * Consumer, producer, and transducer combinators+ splitterToMarker,+ consumeBy, prepend, append, substitute,+ PipeableComponentPair (compose), JoinableComponentPair (join, sequence),+ -- * Pseudo-logic splitter combinators+ -- | Combinators 'sAnd' and 'sOr' are only /pseudo/-logic. While the laws of double negation and De Morgan's laws+ -- hold, 'sAnd' and 'sOr' are in general not commutative, associative, nor idempotent. In the special case when all+ -- argument splitters are stateless, such as those produced by 'Control.Concurrent.SCC.Types.statelessSplitter',+ -- these combinators do satisfy all laws of Boolean algebra.+ sNot, sAnd, sOr,+ -- ** Zipping logic combinators+ -- | The 'pAnd' and 'pOr' combinators run the argument splitters in parallel and combine their logical outputs using+ -- the corresponding logical operation on each output pair, in a manner similar to 'Data.List.zipWith'. They fully+ -- satisfy the laws of Boolean algebra.+ pAnd, pOr,+ -- * Flow-control combinators+ -- | The following combinators resemble the common flow-control programming language constructs. Combinators + -- 'wherever', 'unless', and 'select' are just the special cases of the combinator 'ifs'.+ --+ -- * /transducer/ ``wherever`` /splitter/ = 'ifs' /splitter/ /transducer/ 'Control.Category.id'+ --+ -- * /transducer/ ``unless`` /splitter/ = 'ifs' /splitter/ 'Control.Category.id' /transducer/+ --+ -- * 'select' /splitter/ = 'ifs' /splitter/ 'Control.Category.id'+ -- 'Control.Concurrent.SCC.Primitives.suppress'+ --+ ifs, wherever, unless, select,+ -- ** Recursive+ while, nestedIn,+ -- * Section-based combinators+ -- | All combinators in this section use their 'Control.Concurrent.SCC.Splitter' argument to determine the structure+ -- of the input. Every contiguous portion of the input that gets passed to one or the other sink of the splitter is+ -- treated as one section in the logical structure of the input stream. What is done with the section depends on the+ -- combinator, but the sections, and therefore the logical structure of the input stream, are determined by the+ -- argument splitter alone.+ foreach, having, havingOnly, followedBy, even,+ -- ** first and its variants+ first, uptoFirst, prefix,+ -- ** last and its variants+ last, lastAndAfter, suffix,+ -- ** positional splitters+ startOf, endOf,+ -- ** input ranges+ between,+ -- * parser support+ parseRegions, parseNestedRegions,+ -- * helper functions+ groupMarks, findsTrueIn, findsFalseIn, teeConsumers)+where++import Control.Monad.Coroutine+import Control.Monad.Parallel (MonadParallel(..))++import Control.Concurrent.SCC.Streams+import Control.Concurrent.SCC.Types++import Prelude hiding (even, last, sequence)+import Control.Category ((>>>))+import Control.Monad (liftM, when)+import qualified Control.Monad as Monad+import Control.Monad.Trans (lift)+import Data.Maybe (isJust, isNothing, fromJust)+import qualified Data.Foldable as Foldable+import qualified Data.Sequence as Seq+import Data.Sequence (Seq, (|>), (><), ViewL (EmptyL, (:<)))++import qualified Control.Category+import qualified Data.List++-- | Converts a 'Consumer' into a 'Transducer' with no output.+consumeBy :: forall m x y r. (Monad m) => Consumer m x r -> Transducer m x y+consumeBy c = Transducer $ \ source _sink -> consume c source >> return ()++-- | Class 'PipeableComponentPair' applies to any two components that can be combined into a third component with the+-- following properties:+--+-- * The input of the result, if any, becomes the input of the first component.+--+-- * The output produced by the first child component is consumed by the second child component.+--+-- * The result output, if any, is the output of the second component.+class PipeableComponentPair (m :: * -> *) w c1 c2 c3 | c1 c2 -> c3, c1 c3 -> c2, c2 c3 -> c2,+ c1 -> m w, c2 -> m w, c3 -> m+ where compose :: Bool -> c1 -> c2 -> c3++instance forall m x. (MonadParallel m) =>+ PipeableComponentPair m x (Producer m x ()) (Consumer m x ()) (Performer m ())+ where compose parallel p c = let performPipe :: Coroutine Naught m ((), ())+ performPipe = pipePS parallel (produce p) (consume c)+ in Performer (runCoroutine performPipe >> return ())++instance (MonadParallel m)+ => PipeableComponentPair m y (Transducer m x y) (Consumer m y r) (Consumer m x r)+ where compose parallel t c = isolateConsumer $ \source-> + liftM snd $+ pipePS parallel+ (transduce t source)+ (consume c)++instance (MonadParallel m) => PipeableComponentPair m x (Producer m x r) (Transducer m x y) (Producer m y r)+ where compose parallel p t = isolateProducer $ \sink-> + liftM fst $+ pipePS parallel+ (produce p)+ (\source-> transduce t source sink)++instance MonadParallel m => PipeableComponentPair m y (Transducer m x y) (Transducer m y z) (Transducer m x z)+ where compose parallel t1 t2 = if parallel then t1 >|> t2 else t1 >>> t2++class CompatibleSignature c cons (m :: * -> *) input output | c -> cons m++class AnyListOrUnit c++instance AnyListOrUnit [x]+instance AnyListOrUnit ()++instance (AnyListOrUnit x, AnyListOrUnit y) => CompatibleSignature (Performer m r) (PerformerType r) m x y+instance AnyListOrUnit y => CompatibleSignature (Consumer m x r) (ConsumerType r) m [x] y+instance AnyListOrUnit y => CompatibleSignature (Producer m x r) (ProducerType r) m y [x]+instance CompatibleSignature (Transducer m x y) TransducerType m [x] [y]++data PerformerType r+data ConsumerType r+data ProducerType r+data TransducerType++-- | Class 'JoinableComponentPair' applies to any two components that can be combined into a third component with the+-- following properties:+--+-- * if both argument components consume input, the input of the combined component gets distributed to both+-- components in parallel,+--+-- * if both argument components produce output, the output of the combined component is a concatenation of the+-- complete output from the first component followed by the complete output of the second component, and+--+-- * the 'join' method may apply the components in any order, the 'sequence' method makes sure its first argument+-- has completed before using the second one.+class (Monad m, CompatibleSignature c1 t1 m x y, CompatibleSignature c2 t2 m x y, CompatibleSignature c3 t3 m x y)+ => JoinableComponentPair t1 t2 t3 m x y c1 c2 c3 | c1 c2 -> c3, c1 -> t1 m, c2 -> t2 m, c3 -> t3 m x y,+ t1 m x y -> c1, t2 m x y -> c2, t3 m x y -> c3+ where join :: Bool -> c1 -> c2 -> c3+ sequence :: c1 -> c2 -> c3+ join = const sequence++instance forall m x r1 r2. Monad m =>+ JoinableComponentPair (ProducerType r1) (ProducerType r2) (ProducerType r2) m () [x]+ (Producer m x r1) (Producer m x r2) (Producer m x r2)+ where sequence p1 p2 = Producer $ \sink-> produce p1 sink >> produce p2 sink++instance forall m x. MonadParallel m =>+ JoinableComponentPair (ConsumerType ()) (ConsumerType ()) (ConsumerType ()) m [x] ()+ (Consumer m x ()) (Consumer m x ()) (Consumer m x ())+ where join parallel c1 c2 = Consumer (liftM (const ()) . teeConsumers parallel (consume c1) (consume c2))+ sequence c1 c2 = Consumer $ \source->+ teeConsumers False (consume c1) getList source+ >>= \((), list)-> pipe (putList list) (consume c2)+ >> return ()++instance forall m x y. (MonadParallel m) =>+ JoinableComponentPair TransducerType TransducerType TransducerType m [x] [y]+ (Transducer m x y) (Transducer m x y) (Transducer m x y)+ where join parallel t1 t2 = isolateTransducer $ \source sink->+ pipe+ (\buffer-> teeConsumers parallel+ (\source-> transduce t1 source sink)+ (\source-> transduce t2 source buffer)+ source)+ getList+ >>= \(_, list)-> putList list sink+ sequence t1 t2 = isolateTransducer $ \source sink->+ teeConsumers False (flip (transduce t1) sink) getList source+ >>= \(_, list)-> pipe (putList list) (\source-> transduce t2 source sink)+ >> return ()++instance forall m r1 r2. MonadParallel m =>+ JoinableComponentPair (PerformerType r1) (PerformerType r2) (PerformerType r2) m () ()+ (Performer m r1) (Performer m r2) (Performer m r2)+ where join parallel p1 p2 = Performer $ if parallel+ then bindM2 (const return) (perform p1) (perform p2)+ else perform p1 >> perform p2+ sequence p1 p2 = Performer $ perform p1 >> perform p2++instance forall m x r1 r2. (MonadParallel m) =>+ JoinableComponentPair (PerformerType r1) (ProducerType r2) (ProducerType r2) m () [x]+ (Performer m r1) (Producer m x r2) (Producer m x r2)+ where join parallel pe pr = Producer $ \sink-> if parallel+ then bindM2 (const return) (lift (perform pe)) (produce pr sink)+ else lift (perform pe) >> produce pr sink+ sequence pe pr = Producer $ \sink-> lift (perform pe) >> produce pr sink++instance forall m x r1 r2. (MonadParallel m) =>+ JoinableComponentPair (ProducerType r1) (PerformerType r2) (ProducerType r2) m () [x]+ (Producer m x r1) (Performer m r2) (Producer m x r2)+ where join parallel pr pe = Producer $ \sink-> if parallel+ then bindM2 (const return) (produce pr sink) (lift (perform pe))+ else produce pr sink >> lift (perform pe)+ sequence pr pe = Producer $ \sink-> produce pr sink >> lift (perform pe)++instance forall m x r1 r2. (MonadParallel m) =>+ JoinableComponentPair (PerformerType r1) (ConsumerType r2) (ConsumerType r2) m [x] ()+ (Performer m r1) (Consumer m x r2) (Consumer m x r2)+ where join parallel p c = Consumer $ \source-> if parallel+ then bindM2 (const return) (lift (perform p)) (consume c source)+ else lift (perform p) >> consume c source+ sequence p c = Consumer $ \source-> lift (perform p) >> consume c source++instance forall m x r1 r2. (MonadParallel m) =>+ JoinableComponentPair (ConsumerType r1) (PerformerType r2) (ConsumerType r2) m [x] ()+ (Consumer m x r1) (Performer m r2) (Consumer m x r2)+ where join parallel c p = Consumer $ \source-> if parallel+ then bindM2 (const return) (consume c source) (lift (perform p))+ else consume c source >> lift (perform p)+ sequence c p = Consumer $ \source-> consume c source >> lift (perform p)++instance forall m x y r. (MonadParallel m) =>+ JoinableComponentPair (PerformerType r) TransducerType TransducerType m [x] [y]+ (Performer m r) (Transducer m x y) (Transducer m x y)+ where join parallel p t = Transducer $ \ source sink -> if parallel+ then bindM2 (const return)+ (lift (perform p)) (transduce t source sink)+ else lift (perform p) >> transduce t source sink+ sequence p t = Transducer $ \ source sink -> lift (perform p) >> transduce t source sink++instance forall m x y r. (MonadParallel m)+ => JoinableComponentPair TransducerType (PerformerType r) TransducerType m [x] [y]+ (Transducer m x y) (Performer m r) (Transducer m x y)+ where join parallel t p = Transducer $ \ source sink -> if parallel+ then bindM2 (const . return)+ (transduce t source sink) (lift (perform p))+ else do result <- transduce t source sink+ lift (perform p)+ return result+ sequence t p = Transducer $ \ source sink -> do result <- transduce t source sink+ lift (perform p)+ return result++instance forall m x y. (MonadParallel m) =>+ JoinableComponentPair (ProducerType ()) TransducerType TransducerType m [x] [y]+ (Producer m y ()) (Transducer m x y) (Transducer m x y)+ where join parallel p t = if parallel+ then isolateTransducer $ \source sink->+ do (rest, out) <- pipe+ (\buffer-> bindM2 (const return)+ (produce p sink) (transduce t source buffer))+ getList+ putList out sink+ return rest+ else sequence p t+ sequence p t = Transducer $ \ source sink -> produce p sink >> transduce t source sink++instance forall m x y. (MonadParallel m) =>+ JoinableComponentPair TransducerType (ProducerType ()) TransducerType m [x] [y]+ (Transducer m x y) (Producer m y ()) (Transducer m x y)+ where join parallel t p = if parallel+ then isolateTransducer $ \source sink->+ do (rest, out) <- pipe+ (\buffer-> bindM2 (const . return)+ (transduce t source sink)+ (produce p buffer))+ getList+ putList out sink+ return rest + else sequence t p+ sequence t p = Transducer $ \ source sink -> do result <- transduce t source sink+ produce p sink+ return result++instance forall m x y. (MonadParallel m) =>+ JoinableComponentPair (ConsumerType ()) TransducerType TransducerType m [x] [y]+ (Consumer m x ()) (Transducer m x y) (Transducer m x y)+ where join parallel c t = isolateTransducer $ \source sink->+ teeConsumers parallel (consume c) (\source-> transduce t source sink) source+ >> return ()+ sequence c t = isolateTransducer $ \source sink->+ teeConsumers False (consume c) getList source+ >>= \(_, list)-> pipe (putList list) (\source-> transduce t source sink)+ >> return ()++instance forall m x y. MonadParallel m =>+ JoinableComponentPair TransducerType (ConsumerType ()) TransducerType m [x] [y]+ (Transducer m x y) (Consumer m x ()) (Transducer m x y)+ where join parallel t c = join parallel c t+ sequence t c = isolateTransducer $ \source sink->+ teeConsumers False (\source-> transduce t source sink) getList source+ >>= \(_, list)-> pipe (putList list) (consume c)+ >> return ()++instance forall m x y. (MonadParallel m) =>+ JoinableComponentPair (ProducerType ()) (ConsumerType ()) TransducerType m [x] [y]+ (Producer m y ()) (Consumer m x ()) (Transducer m x y)+ where join parallel p c = Transducer $ \ source sink ->+ if parallel+ then bindM2 (\ _ _ -> return ()) (produce p sink) (consume c source)+ else produce p sink >> consume c source+ sequence p c = Transducer $ \ source sink -> produce p sink >> consume c source++instance forall m x y. (MonadParallel m) =>+ JoinableComponentPair (ConsumerType ()) (ProducerType ()) TransducerType m [x] [y]+ (Consumer m x ()) (Producer m y ()) (Transducer m x y)+ where join parallel c p = join parallel p c+ sequence c p = Transducer $ \ source sink -> consume c source >> produce p sink++-- | Combinator 'prepend' converts the given producer to a 'Control.Concurrent.SCC.Types.Transducer' that passes all its+-- input through unmodified, except for prepending the output of the argument producer to it. The following law holds: @+-- 'prepend' /prefix/ = 'join' ('substitute' /prefix/) 'Control.Category.id' @+prepend :: forall m x r. (Monad m) => Producer m x r -> Transducer m x x+prepend prefix = Transducer $ \ source sink -> produce prefix sink >> pour source sink++-- | Combinator 'append' converts the given producer to a 'Control.Concurrent.SCC.Types.Transducer' that passes all its+-- input through unmodified, finally appending the output of the argument producer to it. The following law holds: @+-- 'append' /suffix/ = 'join' 'Control.Category.id' ('substitute' /suffix/) @+append :: forall m x r. (Monad m) => Producer m x r -> Transducer m x x+append suffix = Transducer $ \ source sink -> pour source sink >> produce suffix sink >> return ()++-- | The 'substitute' combinator converts its argument producer to a 'Control.Concurrent.SCC.Types.Transducer' that+-- produces the same output, while consuming its entire input and ignoring it.+substitute :: forall m x y r. (Monad m) => Producer m y r -> Transducer m x y+substitute feed = Transducer $ \ source sink -> mapMStream_ (const $ return ()) source >> produce feed sink >> return ()++-- | The 'sNot' (streaming not) combinator simply reverses the outputs of the argument splitter. In other words, data+-- that the argument splitter sends to its /true/ sink goes to the /false/ sink of the result, and vice versa.+sNot :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+sNot splitter = isolateSplitter $ \ source true false edge -> suppressProducer (split splitter source false true)++-- | The 'sAnd' combinator sends the /true/ sink output of its left operand to the input of its right operand for+-- further splitting. Both operands' /false/ sinks are connected to the /false/ sink of the combined splitter, but any+-- input value to reach the /true/ sink of the combined component data must be deemed true by both splitters.+sAnd :: forall m x b1 b2. MonadParallel m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)+sAnd parallel s1 s2 =+ isolateSplitter $ \ source true false edge ->+ liftM (fst . fst) $+ pipe+ (\edges-> pipePS parallel+ (\true-> split s1 source true false (mapSink Left edges))+ (\source-> split s2 source true false (mapSink Right edges)))+ (flip intersectRegions edge)++intersectRegions source sink = next Nothing Nothing+ where next lastLeft lastRight = getWith+ (either+ (flip pair lastRight . Just)+ (pair lastLeft . Just))+ source+ pair l@(Just x) r@(Just y) = put sink (x, y)+ >> next Nothing Nothing+ pair l r = next l r++-- | A 'sOr' combinator's input value can reach its /false/ sink only by going through both argument splitters' /false/+-- sinks.+sOr :: forall m x b1 b2. MonadParallel m =>+ Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)+sOr parallel s1 s2 = isolateSplitter $ \ source true false edge ->+ liftM fst $+ pipePS parallel+ (\false-> split s1 source true false (mapSink Left edge))+ (\source-> split s2 source true false (mapSink Right edge))++-- | Combinator 'pAnd' is a pairwise logical conjunction of two splitters run in parallel on the same input.+pAnd :: forall m x b1 b2. MonadParallel m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)+pAnd parallel s1 s2 = isolateSplitter $ \ source true false edge ->+ pipePS parallel+ (transduce (splittersToPairMarker parallel s1 s2) source)+ (\source-> let split l r = getWith (test l r) source+ test l r (Left (x, t1, t2))+ = (if t1 && t2 then put true x else put false x)+ >> split+ (if t1 then l else Nothing)+ (if t2 then r else Nothing)+ test _ Nothing (Right (Left l)) = split (Just l) Nothing+ test _ (Just r) (Right (Left l))+ = put edge (l, r) >> split (Just l) (Just r)+ test Nothing _ (Right (Right r)) = split Nothing (Just r)+ test (Just l) _ (Right (Right r))+ = put edge (l, r) >> split (Just l) (Just r)+ in split Nothing Nothing)+ >> return ()++-- | Combinator 'pOr' is a pairwise logical disjunction of two splitters run in parallel on the same input.+pOr :: forall c m x b1 b2. MonadParallel m =>+ Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (Either b1 b2)+pOr = zipSplittersWith (||) pour++ifs :: forall c m x b. (MonadParallel m, Branching c m x ()) => Bool -> Splitter m x b -> c -> c -> c+ifs parallel s c1 c2 = combineBranches if' parallel c1 c2+ where if' :: forall d. Bool -> (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x ()) ->+ (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x ()) ->+ forall a. OpenConsumer m a d x ()+ if' parallel c1 c2 source = splitInputToConsumers parallel s source c1 c2++wherever :: forall m x b. MonadParallel m => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x+wherever parallel t s = isolateTransducer wherever'+ where wherever' :: forall d. Functor d => Source m d x -> Sink m d x -> Coroutine d m ()+ wherever' source sink = pipePS parallel+ (\true-> split s source true sink (nullSink :: Sink m d b))+ (flip (transduce t) sink)+ >> return ()++unless :: forall m x b. MonadParallel m => Bool -> Transducer m x x -> Splitter m x b -> Transducer m x x+unless parallel t s = wherever parallel t (sNot s)++select :: forall m x b. Monad m => Splitter m x b -> Transducer m x x+select s = isolateTransducer $ \source sink-> suppressProducer (suppressProducer . split s source sink)++-- | Converts a splitter into a parser.+parseRegions :: forall m x b. Monad m => Splitter m x b -> Parser m x b+parseRegions s = isolateTransducer $ \source sink->+ pipe+ (transduce (splitterToMarker s) source)+ (\source-> wrapRegions source sink)+ >> return ()+ where wrapRegions source sink = let wrap Nothing (Left (x, _)) = put sink (Content x)+ >> return Nothing+ wrap (Just p) (Left (x, False)) = flush p+ >> put sink (Content x)+ >> return Nothing+ wrap (Just (b, t)) (Left (x, True)) =+ do Monad.unless t (put sink (Markup (Start b)))+ put sink (Content x)+ return (Just (b, True))+ wrap (Just p) (Right b') = flush p >> return (Just (b', False))+ wrap Nothing (Right b) = return (Just (b, False))+ flush (b, t) = put sink $ Markup $ (if t then End else Point) b+ in foldMStream wrap Nothing source >>= maybe (return ()) flush++-- | Converts a boundary-marking splitter into a parser.+parseNestedRegions :: forall m x b. Monad m => Splitter m x (Boundary b) -> Parser m x b+parseNestedRegions s = isolateTransducer $ \source sink->+ split s source (mapSink Content sink) (mapSink Content sink) (mapSink Markup sink)++-- | The recursive combinator 'while' feeds the true sink of the argument splitter back to itself, modified by the+-- argument transducer. Data fed to the splitter's false sink is passed on unmodified.+while :: forall m x b. MonadParallel m => [(Bool, (Transducer m x x, Splitter m x b))] -> Transducer m x x+while ((parallel, (t, s)) : rest) = isolateTransducer while'+ where while' :: forall d. Functor d => Source m d x -> Sink m d x -> Coroutine d m ()+ while' source sink =+ pipePS parallel+ (\true-> split s source true sink (nullSink :: Sink m d b))+ (\source-> getWith+ (\x-> liftM fst $+ pipe+ (\sink-> put sink x >> pour source sink)+ (\source-> transduce while'' source sink))+ source)+ >> return ()+ while'' = compose parallel t (while rest)++-- | The recursive combinator 'nestedIn' combines two splitters into a mutually recursive loop acting as a single+-- splitter. The true sink of one of the argument splitters and false sink of the other become the true and false sinks+-- of the loop. The other two sinks are bound to the other splitter's source. The use of 'nestedIn' makes sense only+-- on hierarchically structured streams. If we gave it some input containing a flat sequence of values, and assuming+-- both component splitters are deterministic and stateless, an input value would either not loop at all or it would+-- loop forever.+nestedIn :: forall m x b. MonadParallel m => [(Bool, (Splitter m x b, Splitter m x b))] -> Splitter m x b+nestedIn ((parallel, (s1, s2)) : rest) =+ isolateSplitter $ \ source true false edge ->+ liftM fst $+ pipePS parallel+ (\false-> split s1 source true false edge)+ (\source-> pipe+ (\true-> split s2 source true false (filterMSink (const $ return False) edge))+ (\source-> get source+ >>= maybe+ (return ((), ()))+ (\x-> pipe+ (\sink-> put sink x >> pour source sink)+ (\source-> split (nestedIn rest) source true false edge))))++-- | The 'foreach' combinator is similar to the combinator 'ifs' in that it combines a splitter and two transducers into+-- another transducer. However, in this case the transducers are re-instantiated for each consecutive portion of the+-- input as the splitter chunks it up. Each contiguous portion of the input that the splitter sends to one of its two+-- sinks gets transducered through the appropriate argument transducer as that transducer's whole input. As soon as the+-- contiguous portion is finished, the transducer gets terminated.+foreach :: forall m x b c. (MonadParallel m, Branching c m x ()) => Bool -> Splitter m x b -> c -> c -> c+foreach parallel s c1 c2 = combineBranches foreach' parallel c1 c2+ where foreach' :: forall d. Bool -> + (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x ()) ->+ (forall a d'. AncestorFunctor d d' => OpenConsumer m a d' x ()) ->+ forall a. OpenConsumer m a d x ()+ foreach' parallel c1 c2 source =+ liftM fst $+ pipePS parallel+ (transduce (splitterToMarker s) (liftSource source :: Source m d x))+ (\source-> groupMarks source (maybe c2 (const c1)))++-- | The 'having' combinator combines two pure splitters into a pure splitter. One splitter is used to chunk the input+-- into contiguous portions. Its /false/ sink is routed directly to the /false/ sink of the combined splitter. The+-- second splitter is instantiated and run on each portion of the input that goes to first splitter's /true/ sink. If+-- the second splitter sends any output at all to its /true/ sink, the whole input portion is passed on to the /true/+-- sink of the combined splitter, otherwise it goes to its /false/ sink.+having :: forall m x b1 b2. MonadParallel m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1+having parallel s1 s2 = isolateSplitter s+ where s source true false edge = pipePS parallel+ (transduce (splitterToMarker s1) source)+ (flip groupMarks test)+ >> return ()+ where test Nothing chunk = pour chunk false+ test (Just mb) chunk = teeConsumers False getList (findsTrueIn s2) chunk+ >>= \(chunk, maybeFound)->+ if isJust maybeFound+ then maybe (return ()) (put edge) mb+ >> putList chunk true+ else putList chunk false++-- | The 'havingOnly' combinator is analogous to the 'having' combinator, but it succeeds and passes each chunk of the+-- input to its /true/ sink only if the second splitter sends no part of it to its /false/ sink.+havingOnly :: forall m x b1 b2. MonadParallel m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1+havingOnly parallel s1 s2 = isolateSplitter s+ where s source true false edge = pipePS parallel+ (transduce (splitterToMarker s1) source)+ (flip groupMarks test)+ >> return ()+ where test Nothing chunk = pour chunk false+ test (Just mb) chunk = teeConsumers False getList (findsFalseIn s2) chunk+ >>= \(chunk, anyFalse)->+ if anyFalse+ then putList chunk false+ else maybe (return ()) (put edge) mb+ >> putList chunk true++-- | The result of combinator 'first' behaves the same as the argument splitter up to and including the first portion of+-- the input which goes into the argument's /true/ sink. All input following the first true portion goes into the+-- /false/ sink.+first :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+first splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split 1 (Left (x, False)) = put false x >> return 1+ split 1 (Left (x, True)) = put true x >> return 2+ split 1 (Right b) = put edge b >> return 2+ split 2 b@Right{} = return 3+ split 2 (Left (x, True)) = put true x >> return 2+ split 2 (Left (x, False)) = put false x >> return 3+ split 3 (Left (x, _)) = put false x >> return 3+ split 3 (Right _) = return 3+ in foldMStream_ split 1 source++-- | The result of combinator 'uptoFirst' takes all input up to and including the first portion of the input which goes+-- into the argument's /true/ sink and feeds it to the result splitter's /true/ sink. All the rest of the input goes+-- into the /false/ sink. The only difference between 'first' and 'uptoFirst' combinators is in where they direct the+-- /false/ portion of the input preceding the first /true/ part.+uptoFirst :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+uptoFirst splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split (Left q) (Left (x, False)) = return (Left (q |> x))+ split (Left q) (Left (x, True)) = putQueue q true+ >> put true x+ >> return (Right True)+ split (Left q) (Right b) = putQueue q true+ >> put edge b+ >> return (Right True)+ split (Right True) Right{} = return (Right False)+ split (Right True) (Left (x, True)) = put true x >> return (Right True)+ split (Right True) (Left (x, False)) = put false x >> return (Right False)+ split (Right False) (Left (x, _)) = put false x >> return (Right False)+ split (Right False) (Right _) = return (Right False)+ in foldMStream split (Left Seq.empty) source+ >>= either (flip putQueue false) (const $ return ())++-- | The result of the combinator 'last' is a splitter which directs all input to its /false/ sink, up to the last+-- portion of the input which goes to its argument's /true/ sink. That portion of the input is the only one that goes to+-- the resulting component's /true/ sink. The splitter returned by the combinator 'last' has to buffer the previous two+-- portions of its input, because it cannot know if a true portion of the input is the last one until it sees the end of+-- the input or another portion succeeding the previous one.+last :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+last splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let get1 (Left (x, False)) = put false x+ >> getWith get1 source+ get1 p@(Left (x, True)) = get2 Nothing Seq.empty p+ get1 (Right b) = getWith (get2 (Just b) Seq.empty) source+ get2 mb q (Left (x, True)) = let q' = q |> x+ in get source+ >>= maybe+ (flush mb q')+ (get2 mb q')+ get2 mb q p = get3 mb q Seq.empty p+ get3 mb qt qf (Left (x, False)) =+ let qf' = qf |> x+ in get source+ >>= maybe+ (flush mb qt >> putQueue qf' false)+ (get3 mb qt qf')+ get3 mb qt qf p = do putQueue qt false+ putQueue qf false+ get1 p+ flush mb q = maybe (return ()) (put edge) mb+ >> putQueue q true+ in getWith get1 source++-- | The result of the combinator 'lastAndAfter' is a splitter which directs all input to its /false/ sink, up to the+-- last portion of the input which goes to its argument's /true/ sink. That portion and the remainder of the input is+-- fed to the resulting component's /true/ sink. The difference between 'last' and 'lastAndAfter' combinators is where+-- they feed the /false/ portion of the input, if any, remaining after the last /true/ part.+lastAndAfter :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+lastAndAfter splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let get1 (Left (x, False)) = put false x+ >> getWith get1 source+ get1 p@(Left (x, True)) = get2 Nothing Seq.empty p+ get1 (Right b) = getWith (get2 (Just b) Seq.empty) source+ get2 mb q (Left (x, True)) = let q' = q |> x+ in get source+ >>= maybe+ (flush mb q')+ (get2 mb q')+ get2 mb q p = get3 mb q p+ get3 mb q (Left (x, False)) = let q' = q |> x+ in get source+ >>= maybe+ (flush mb q')+ (get3 mb q')+ get3 _ q p@(Left (x, True)) = putQueue q false+ >> get1 p+ get3 _ q b'@Right{} = putQueue q false+ >> get1 b'+ flush mb q = maybe (return ()) (put edge) mb+ >> putQueue q true+ in getWith get1 source++-- | The 'prefix' combinator feeds its /true/ sink only the prefix of the input that its argument feeds to its /true/+-- sink. All the rest of the input is dumped into the /false/ sink of the result.+prefix :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+prefix splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split 0 p@Left{} = split 1 p+ split 0 (Right b) = put edge b >> return 1+ split 1 (Left (x, False)) = put false x >> return 2+ split 1 (Left (x, True)) = put true x >> return 1+ split 1 (Right b) = return 2+ split 2 (Left (x, _)) = put false x >> return 2+ split 2 Right{} = return 2+ in foldMStream_ split 0 source++-- | The 'suffix' combinator feeds its /true/ sink only the suffix of the input that its argument feeds to its /true/+-- sink. All the rest of the input is dumped into the /false/ sink of the result.+suffix :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+suffix splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split Nothing (Left (x, False)) = put false x >> return Nothing+ split Nothing (Left (x, True)) = return (Just (Nothing, Seq.singleton x))+ split Nothing (Right b) = return (Just (Just b, Seq.empty))+ split (Just (mb, q)) (Left (x, True)) = return (Just (mb, q |> x))+ split (Just (mb, q)) (Left (x, False)) = putQueue q false+ >> put false x+ >> return Nothing+ split (Just (mb, q)) (Right b) = putQueue q false+ >> return (Just (Just b, Seq.empty))+ in foldMStream split Nothing source+ >>= \r-> case r of Nothing -> return ()+ Just (Nothing, q) -> putQueue q true+ Just (Just b, q) -> put edge b >> putQueue q true++-- | The 'even' combinator takes every input section that its argument /splitter/ deems /true/, and feeds even ones into+-- its /true/ sink. The odd sections and parts of input that are /false/ according to its argument splitter are fed to+-- 'even' splitter's /false/ sink.+even :: forall m x b. Monad m => Splitter m x b -> Splitter m x b+even splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split 1 (Left (x, False)) = put false x >> return 1+ split 1 p@(Left (x, True)) = split 2 p+ split 1 (Right b) = return 2+ split 2 (Left (x, True)) = put false x >> return 2+ split 2 p@(Left (x, False)) = split 3 p+ split 2 (Right b) = put edge b >> return 4+ split 3 (Left (x, False)) = put false x >> return 3+ split 3 p@(Left (x, True)) = split 4 p+ split 3 (Right b) = put edge b >> return 4+ split 4 (Left (x, True)) = put true x >> return 4+ split 4 p@(Left (x, False)) = split 1 p+ split 4 (Right b) = return 2+ in foldMStream_ split 1 source++-- | Splitter 'startOf' issues an empty /true/ section at the beginning of every section considered /true/ by its+-- argument splitter, otherwise the entire input goes into its /false/ sink.+startOf :: forall m x b. Monad m => Splitter m x b -> Splitter m x (Maybe b)+startOf splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split 1 (Left (x, False)) = put false x >> return 1+ split 1 p@(Left (x, True)) = put edge Nothing >> split 2 p+ split 1 (Right b) = put edge (Just b) >> return 2+ split 2 (Left (x, True)) = put false x >> return 2+ split 2 p = split 1 p+ in foldMStream_ split 1 source++-- | Splitter 'endOf' issues an empty /true/ section at the end of every section considered /true/ by its argument+-- splitter, otherwise the entire input goes into its /false/ sink.+endOf :: forall m x b. Monad m => Splitter m x b -> Splitter m x (Maybe b)+endOf splitter = wrapMarkedSplitter splitter $+ \source true false edge->+ let split Nothing (Left (x, False)) = put false x >> return Nothing+ split Nothing p@(Left (x, True)) = split (Just Nothing) p+ split Nothing (Right b) = return (Just (Just b))+ split (Just mb) (Left (x, True)) = put false x >> return (Just mb)+ split (Just mb) p@(Left (x, False)) = put edge mb >> split Nothing p+ split (Just mb) (Right b) = put edge mb >> return (Just $ Just b)+ in foldMStream split Nothing source >>= maybe (return ()) (put edge)++-- | Combinator 'followedBy' treats its argument 'Splitter's as patterns components and returns a 'Splitter' that+-- matches their concatenation. A section of input is considered /true/ by the result iff its prefix is considered+-- /true/ by argument /s1/ and the rest of the section is considered /true/ by /s2/. The splitter /s2/ is started anew+-- after every section split to /true/ sink by /s1/.+followedBy :: forall m x b1 b2. MonadParallel m =>+ Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x (b1, b2)+followedBy parallel s1 s2 = + isolateSplitter $ \ source true false edge ->+ pipePS parallel+ (transduce (splitterToMarker s1) source)+ (\source-> let get0 q = case Seq.viewl q+ of Seq.EmptyL -> getWith get1 source+ (Left (x, False)) :< rest -> put false x+ >> get0 rest+ (Left (x, True)) :< rest -> get2 Nothing Seq.empty q+ (Right b) :< rest -> get2 (Just b) Seq.empty rest+ get1 (Left (x, False)) = put false x+ >> getWith get1 source+ get1 p@(Left (x, True)) = get2 Nothing Seq.empty (Seq.singleton p)+ get1 (Right b) = get2 (Just b) Seq.empty Seq.empty+ get2 mb q q' = case Seq.viewl q'+ of Seq.EmptyL -> get source+ >>= maybe (testEnd mb q) (get2 mb q . Seq.singleton)+ (Left (x, True)) :< rest -> get2 mb (q |> x) rest+ (Left (x, False)) :< rest -> get3 mb q q'+ Right{} :< rest -> get3 mb q q'+ get3 mb q q' = do ((q1, q2), n) <- pipe (get7 Seq.empty q') (test mb q)+ case n of Nothing -> putQueue q false+ >> get0 (q1 >< q2)+ Just 0 -> get0 (q1 >< q2)+ Just n -> get8 (Just mb) n (q1 >< q2)+ get7 q1 q2 sink = case Seq.viewl q2+ of Seq.EmptyL -> get source+ >>= maybe (return (q1, q2))+ (\p-> either+ (put sink . fst)+ (const $ return ())+ p+ >> get7 (q1 |> p) q2 sink)+ p :< rest -> either+ (put sink . fst)+ (const $ return ()) p+ >> get7 (q1 |> p) rest sink+ testEnd mb q = do ((), n) <- pipe (const $ return ()) (test mb q)+ case n of Nothing -> putQueue q false+ _ -> return ()+ test mb q source = liftM snd $+ pipe+ (transduce (splitterToMarker s2) source)+ (\source-> let get4 (Left (_, False)) = return Nothing+ get4 p@(Left (_, True)) = putQueue q true+ >> get5 0 p+ get4 p@(Right b) = maybe+ (return ())+ (\b1-> put edge (b1, b))+ mb+ >> putQueue q true+ >> get6 0+ get5 n (Left (x, True)) = put true x+ >> get6 (succ n)+ get5 n _ = return (Just n)+ get6 n = get source+ >>= maybe+ (return $ Just n)+ (get5 n)+ in get source >>= maybe (return Nothing) get4)+ get8 Nothing 0 q = get0 q+ get8 (Just mb) 0 q = get2 mb Seq.empty q+ get8 mmb n q = case Seq.viewl q of Left (x, False) :< rest -> get8 Nothing (pred n) rest+ Left (x, True) :< rest+ -> get8 (maybe (Just Nothing) Just mmb) (pred n) rest+ Right b :< rest -> get8 (Just (Just b)) n rest+ in get0 Seq.empty)+ >> return ()++-- | Combinator '...' tracks the running balance of difference between the number of preceding starts of sections+-- considered /true/ according to its first argument and the ones according to its second argument. The combinator+-- passes to /true/ all input values for which the difference balance is positive. This combinator is typically used+-- with 'startOf' and 'endOf' in order to count entire input sections and ignore their lengths.+between :: forall m x b1 b2. MonadParallel m => Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b1+between parallel s1 s2 = isolateSplitter $ \ source true false edge ->+ pipePS parallel+ (transduce (splittersToPairMarker parallel s1 s2) source)+ (let pass n x = (if n > 0 then put true x else put false x)+ >> return n+ pass' n x = (if n >= 0 then put true x else put false x)+ >> return n+ state n (Left (x, True, False)) = pass (succ n) x+ state n (Left (x, False, True)) = pass' (pred n) x+ state n (Left (x, True, True)) = pass' n x+ state n (Left (x, False, False)) = pass n x+ state 0 (Right (Left b)) = put edge b >> return 1+ state n (Right (Left _)) = return (succ n)+ state n (Right (Right _)) = return (pred n)+ in foldMStream_ state 0)+ >> return ()++-- Helper functions++wrapMarkedSplitter ::+ forall m x b1 b2. Monad m =>+ Splitter m x b1+ -> (forall a1 a2 a3 a4 d. (AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d, AncestorFunctor a4 d) =>+ Source m a1 (Either (x, Bool) b1) -> Sink m a2 x -> Sink m a3 x -> Sink m a4 b2 -> Coroutine d m ())+ -> Splitter m x b2+wrapMarkedSplitter splitter splitMarked = isolateSplitter $ \ source true false edge ->+ pipe+ (transduce (splitterToMarker splitter) source)+ (\source-> splitMarked source true false edge)+ >> return ()++splitterToMarker :: forall m x b. Monad m => Splitter m x b -> Transducer m x (Either (x, Bool) b)+splitterToMarker s = isolateTransducer $ \source sink->+ split s source+ (mapSink (\x-> Left (x, True)) sink)+ (mapSink (\x-> Left (x, False)) sink)+ (mapSink Right sink)++splittersToPairMarker :: forall m x b1 b2. (MonadParallel m) => Bool -> Splitter m x b1 -> Splitter m x b2 ->+ Transducer m x (Either (x, Bool, Bool) (Either b1 b2))+splittersToPairMarker parallel s1 s2 =+ let t source sink = + pipe+ (\sync-> teeConsumers parallel+ (\source1-> split s1 source1+ (mapSink (\x-> Left ((x, True), True)) sync)+ (mapSink (\x-> Left ((x, False), True)) sync)+ (mapSink (Right. Left) sync))+ (\source2-> split s2 source2+ (mapSink (\x-> Left ((x, True), False)) sync)+ (mapSink (\x-> Left ((x, False), False)) sync)+ (mapSink (Right . Right) sync))+ source)+ (synchronizeMarks sink)+ >> return ()+ synchronizeMarks :: forall m a1 a2 d. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) =>+ Sink m a1 (Either (x, Bool, Bool) (Either b1 b2))+ -> Source m a2 (Either ((x, Bool), Bool) (Either b1 b2))+ -> Coroutine d m ()+ synchronizeMarks sink source = foldMStream handleMark Nothing source >>= \Nothing-> return () where+ handleMark Nothing (Right b) = put sink (Right b) >> return Nothing+ handleMark Nothing (Left (p, first)) = return (Just (Seq.singleton (Left p), first))+ handleMark state@(Just (q, first)) (Left (p, first')) | first == first' = return (Just (q |> Left p, first))+ handleMark state@(Just (q, True)) (Right b@Left{}) = return (Just (q |> Right b, True))+ handleMark state@(Just (q, False)) (Right b@Right{}) = return (Just (q |> Right b, False))+ handleMark state (Right b) = put sink (Right b) >> return state+ handleMark state@(Just (q, pos')) mark@(Left ((x, t), pos))+ = case Seq.viewl q+ of Seq.EmptyL -> return (Just (Seq.singleton (Left (x, t)), pos))+ Right b :< rest -> put sink (Right b)+ >> handleMark (if Seq.null rest then Nothing else Just (rest, pos')) mark+ Left (y, t') :< rest -> put sink (Left $ if pos then (y, t, t') else (y, t', t))+ >> return (if Seq.null rest then Nothing else Just (rest, pos'))+ returnQueuedList q = return $ concatMap (either ((:[]) . fst) (const [])) $ Foldable.toList $ Seq.viewl q+ in isolateTransducer t++zipSplittersWith :: forall m x b1 b2 b. MonadParallel m => + (Bool -> Bool -> Bool) -> + (forall a1 a2 d. (AncestorFunctor a1 d, AncestorFunctor a2 d) =>+ Source m a1 (Either b1 b2) -> Sink m a2 b -> Coroutine d m ()) -> + Bool -> Splitter m x b1 -> Splitter m x b2 -> Splitter m x b+zipSplittersWith f boundaries parallel s1 s2+ = isolateSplitter $ \ source true false edge ->+ pipe+ (\edge->+ pipePS parallel+ (transduce (splittersToPairMarker parallel s1 s2) source)+ (mapMStream_+ (either+ (\(x, t1, t2)-> if f t1 t2 then put true x else put false x)+ (put edge))))+ (flip boundaries edge)+ >> return ()++-- | Runs the second argument on every contiguous region of input source (typically produced by 'splitterToMarker')+-- whose all values either match @Left (_, True)@ or @Left (_, False)@.+groupMarks :: (Monad m, AncestorFunctor a d, AncestorFunctor a (SinkFunctor d x)) =>+ Source m a (Either (x, Bool) b) ->+ (Maybe (Maybe b) -> Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m r) ->+ Coroutine d m ()+groupMarks source getConsumer = start+ where start = getWith (either startContent startRegion) source+ startContent (x, False) = pipe (\sink-> pass False sink x) (getConsumer Nothing)+ >>= maybe (return ()) (either startContent startRegion) . fst+ startContent (x, True) = pipe (\sink-> pass True sink x) (getConsumer $ Just Nothing)+ >>= maybe (return ()) (either startContent startRegion) . fst+ startRegion b = pipe (next True) (getConsumer (Just $ Just b))+ >>= maybe (return ()) (either startContent startRegion) . fst+ pass t sink x = put sink x >> next t sink+ next t sink = get source >>= maybe (return Nothing) (continue t sink)+ continue t sink (Left (x, t')) | t == t' = pass t sink x+ continue t sink p = return (Just p)++-- | 'suppressProducer' runs the /producer/ argument with a new sink, suppressing everything 'put' in the sink.+suppressProducer :: forall m a x r. (Functor a, Monad m) => (Sink m a x -> Coroutine a m r) -> Coroutine a m r+suppressProducer producer = producer (nullSink :: Sink m a x)++findsTrueIn :: forall m a d x b. (Monad m, AncestorFunctor a d)+ => Splitter m x b -> Source m a x -> Coroutine d m (Maybe (Maybe b))+findsTrueIn splitter source = pipe+ (\testTrue-> pipe+ (split splitter (liftSource source :: Source m d x)+ testTrue+ (nullSink :: Sink m d x))+ get)+ get+ >>= \(((), maybeEdge), maybeTrue)-> return $+ case maybeEdge+ of Nothing -> fmap (const Nothing) maybeTrue+ _ -> Just maybeEdge++findsFalseIn :: forall m a d x b. (Monad m, AncestorFunctor a d) => Splitter m x b -> Source m a x -> Coroutine d m Bool+findsFalseIn splitter source = pipe+ (\testFalse-> split splitter (liftSource source :: Source m d x)+ (nullSink :: Sink m d x)+ testFalse+ (nullSink :: Sink m d b))+ get+ >>= \((), maybeFalse)-> return (isJust maybeFalse)++teeConsumers :: forall m a d x r1 r2. MonadParallel m+ => Bool -> (forall a. OpenConsumer m a (SinkFunctor d x) x r1)+ -> (forall a. OpenConsumer m a (SourceFunctor d x) x r2)+ -> OpenConsumer m a d x (r1, r2)+teeConsumers parallel c1 c2 source = pipePS parallel consume1 c2+ where consume1 sink = c1 (teeSource sink source' :: Source m (SinkFunctor d x) x)+ source' :: Source m d x+ source' = liftSource source
Control/Concurrent/SCC/Components.hs view
@@ -1,5 +1,5 @@ {- - Copyright 2008-2009 Mario Blazevic+ Copyright 2008-2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. @@ -22,8 +22,11 @@ module Control.Concurrent.SCC.Components where -import Control.Concurrent.Coroutine+import Control.Monad.Coroutine+import Control.Monad.Parallel (MonadParallel(..))+ import Control.Concurrent.SCC.Types+import Control.Concurrent.SCC.Types as Types import qualified Control.Concurrent.SCC.Combinators as Combinator import qualified Control.Concurrent.SCC.Primitives as Primitive import qualified Control.Concurrent.SCC.XML as XML@@ -31,7 +34,8 @@ import Control.Concurrent.SCC.XML (Token) import Control.Concurrent.Configuration -import Prelude hiding (appendFile, even, last, sequence, (||), (&&))+import Prelude hiding (appendFile, even, id, last, sequence, (||), (&&))+import qualified Control.Category import Control.Monad (liftM) import System.IO (Handle)@@ -65,7 +69,7 @@ toList = atomic "toList" 1 Primitive.toList -- | 'fromList' produces the contents of the given list argument.-fromList :: forall m x. Monad m => [x] -> ProducerComponent m x [x]+fromList :: forall m x. Monad m => [x] -> ProducerComponent m x () fromList l = atomic "fromList" 1 (Primitive.fromList l) -- | ConsumerComponent 'toStdOut' copies the given source into the standard output.@@ -98,9 +102,9 @@ toHandle :: Handle -> Bool -> ConsumerComponent IO Char () toHandle handle doClose = atomic "toHandle" ioCost (Primitive.toHandle handle doClose) --- | TransducerComponent 'asis' passes its input through unmodified.-asis :: forall m x. Monad m => TransducerComponent m x x-asis = atomic "asis" 1 Primitive.asis+-- | TransducerComponent 'id' passes its input through unmodified.+id :: forall m x. Monad m => TransducerComponent m x x+id = atomic "id" 1 Control.Category.id -- | TransducerComponent 'unparse' removes all markup from its input and passes the content through. unparse :: forall m x y. Monad m => TransducerComponent m (Markup y x) x@@ -226,7 +230,7 @@ -- * The result output, if any, is the output of the second component. (>->) :: Combinator.PipeableComponentPair m w c1 c2 c3 => Component c1 -> Component c2 -> Component c3-(>->) = liftParallelPair ">->" Combinator.connect+(>->) = liftParallelPair ">->" Combinator.compose class CompatibleSignature c cons (m :: * -> *) input output | c -> cons m @@ -287,35 +291,35 @@ -- | The '>&' combinator sends the /true/ sink output of its left operand to the input of its right operand for further -- splitting. Both operands' /false/ sinks are connected to the /false/ sink of the combined splitter, but any input -- value to reach the /true/ sink of the combined component data must be deemed true by both splitters.-(>&) :: forall m x b1 b2. ParallelizableMonad m =>+(>&) :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2) (>&) = liftParallelPair ">&" Combinator.sAnd -- | A '>|' combinator's input value can reach its /false/ sink only by going through both argument splitters' /false/ -- sinks.-(>|) :: forall m x b1 b2. ParallelizableMonad m =>+(>|) :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (Either b1 b2) (>|) = liftParallelPair ">&" Combinator.sOr -- | Combinator '&&' is a pairwise logical conjunction of two splitters run in parallel on the same input.-(&&) :: forall m x b1 b2. ParallelizableMonad m =>+(&&) :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2) (&&) = liftParallelPair "&&" Combinator.pAnd -- | Combinator '||' is a pairwise logical disjunction of two splitters run in parallel on the same input.-(||) :: (ParallelizableMonad m)+(||) :: (MonadParallel m) => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (Either b1 b2) (||) = liftParallelPair "||" Combinator.pOr -ifs :: forall c m x b. (ParallelizableMonad m, Branching c m x [x]) =>+ifs :: forall c m x b. (MonadParallel m, Branching c m x ()) => SplitterComponent m x b -> Component c -> Component c -> Component c ifs = parallelRouterAndBranches "ifs" Combinator.ifs -wherever :: forall m x b. ParallelizableMonad m =>+wherever :: forall m x b. MonadParallel m => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x wherever = liftParallelPair "wherever" Combinator.wherever -unless :: forall m x b. ParallelizableMonad m =>+unless :: forall m x b. MonadParallel m => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x unless = liftParallelPair "unless" Combinator.unless @@ -327,13 +331,13 @@ parseRegions = lift 1 "parseRegions" Combinator.parseRegions -- | Converts a boundary-marking splitter into a parser.-parseNestedRegions :: forall m x b. ParallelizableMonad m =>+parseNestedRegions :: forall m x b. MonadParallel m => SplitterComponent m x (Boundary b) -> ParserComponent m x b parseNestedRegions = lift 1 "parseNestedRegions" Combinator.parseNestedRegions -- | The recursive combinator 'while' feeds the true sink of the argument splitter back to itself, modified by the -- argument transducer. Data fed to the splitter's false sink is passed on unmodified.-while :: forall m x b. ParallelizableMonad m =>+while :: forall m x b. MonadParallel m => TransducerComponent m x x -> SplitterComponent m x b -> TransducerComponent m x x while t s = recursiveComponentTree "while" Combinator.while $ liftSequentialPair "pair" (,) t s @@ -343,7 +347,7 @@ -- on hierarchically structured streams. If we gave it some input containing a flat sequence of values, and assuming -- both component splitters are deterministic and stateless, an input value would either not loop at all or it would -- loop forever.-nestedIn :: forall m x b. ParallelizableMonad m =>+nestedIn :: forall m x b. MonadParallel m => SplitterComponent m x b -> SplitterComponent m x b -> SplitterComponent m x b nestedIn s1 s2 = recursiveComponentTree "nestedIn" Combinator.nestedIn $ liftSequentialPair "pair" (,) s1 s2 @@ -352,7 +356,7 @@ -- input as the splitter chunks it up. Each contiguous portion of the input that the splitter sends to one of its two -- sinks gets transducered through the appropriate argument transducer as that transducer's whole input. As soon as the -- contiguous portion is finished, the transducer gets terminated.-foreach :: forall m x b c. (ParallelizableMonad m, Branching c m x [x]) =>+foreach :: forall m x b c. (MonadParallel m, Branching c m x ()) => SplitterComponent m x b -> Component c -> Component c -> Component c foreach = parallelRouterAndBranches "foreach" Combinator.foreach @@ -361,13 +365,13 @@ -- second splitter is instantiated and run on each portion of the input that goes to first splitter's /true/ sink. If -- the second splitter sends any output at all to its /true/ sink, the whole input portion is passed on to the /true/ -- sink of the combined splitter, otherwise it goes to its /false/ sink.-having :: forall m x b1 b2. ParallelizableMonad m =>+having :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1 having = liftParallelPair "having" Combinator.having -- | The 'havingOnly' combinator is analogous to the 'having' combinator, but it succeeds and passes each chunk of the -- input to its /true/ sink only if the second splitter sends no part of it to its /false/ sink.-havingOnly :: forall m x b1 b2. ParallelizableMonad m =>+havingOnly :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1 havingOnly = liftParallelPair "havingOnly" Combinator.havingOnly @@ -422,14 +426,14 @@ -- | SplitterComponent 'endOf' issues an empty /true/ section at the end of every section considered /true/ by its -- argument splitter, otherwise the entire input goes into its /false/ sink.-endOf :: forall m x b. ParallelizableMonad m => SplitterComponent m x b -> SplitterComponent m x (Maybe b)+endOf :: forall m x b. MonadParallel m => SplitterComponent m x b -> SplitterComponent m x (Maybe b) endOf = lift 2 "endOf" Combinator.endOf -- | Combinator 'followedBy' treats its argument 'SplitterComponent's as patterns components and returns a 'SplitterComponent' that -- matches their concatenation. A section of input is considered /true/ by the result iff its prefix is considered -- /true/ by argument /s1/ and the rest of the section is considered /true/ by /s2/. The splitter /s2/ is started anew -- after every section split to /true/ sink by /s1/.-followedBy :: forall m x b1 b2. ParallelizableMonad m =>+followedBy :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x (b1, b2) followedBy = liftParallelPair "followedBy" Combinator.followedBy @@ -437,7 +441,7 @@ -- considered /true/ according to its first argument and the ones according to its second argument. The combinator -- passes to /true/ all input values for which the difference balance is positive. This combinator is typically used -- with 'startOf' and 'endOf' in order to count entire input sections and ignore their lengths.-(...) :: forall m x b1 b2. ParallelizableMonad m =>+(...) :: forall m x b1 b2. MonadParallel m => SplitterComponent m x b1 -> SplitterComponent m x b2 -> SplitterComponent m x b1 (...) = liftParallelPair "..." Combinator.between @@ -455,7 +459,7 @@ -- | Similiar to @('Control.Concurrent.SCC.Combinators.having' 'element')@, except it runs the argument splitter -- only on each element's start tag, not on the entire element with its content.-xmlElementHavingTag :: forall m b. ParallelizableMonad m =>+xmlElementHavingTag :: forall m b. MonadParallel m => SplitterComponent m (Markup Token Char) b -> SplitterComponent m (Markup Token Char) b xmlElementHavingTag = lift 2 "XML.elementHavingTag" XML.elementHavingTag @@ -476,12 +480,12 @@ xmlAttributeValue :: Monad m => SplitterComponent m (Markup Token Char) () xmlAttributeValue = atomic "XML.attributeValue" 1 XML.attributeValue -xmlHavingText :: forall m b1 b2. ParallelizableMonad m =>+xmlHavingText :: forall m b1 b2. MonadParallel m => SplitterComponent m (Markup Token Char) b1 -> SplitterComponent m Char b2 -> SplitterComponent m (Markup Token Char) b1 xmlHavingText = liftParallelPair "XML.havingText" XML.havingText -xmlHavingOnlyText :: forall m b1 b2. ParallelizableMonad m =>+xmlHavingOnlyText :: forall m b1 b2. MonadParallel m => SplitterComponent m (Markup Token Char) b1 -> SplitterComponent m Char b2 -> SplitterComponent m (Markup Token Char) b1 xmlHavingOnlyText = liftParallelPair "XML.havingOnlyText" XML.havingOnlyText
Control/Concurrent/SCC/Primitives.hs view
@@ -1,5 +1,5 @@ {- - Copyright 2008-2009 Mario Blazevic+ Copyright 2008-2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. @@ -31,13 +31,13 @@ -- * Generic consumers suppress, erroneous, -- * Generic transducers- asis, parse, unparse, parseSubstring,+ parse, unparse, parseSubstring, -- * Generic splitters everything, nothing, marked, markedContent, markedWith, contentMarkedWith, one, substring, -- * List transducers -- | The following laws hold: --- -- * 'group' '>->' 'concatenate' == 'asis'+ -- * 'group' '>>>' 'concatenate' == 'id' -- -- * 'concatenate' == 'concatSeparate' [] group, concatenate, concatSeparate,@@ -50,7 +50,7 @@ import Prelude hiding (appendFile) -import Control.Concurrent.Coroutine+import Control.Monad.Coroutine import Control.Concurrent.SCC.Streams import Control.Concurrent.SCC.Types @@ -74,25 +74,16 @@ toList = Consumer getList -- | 'fromList' produces the contents of the given list argument.-fromList :: forall m x. Monad m => [x] -> Producer m x [x]+fromList :: forall m x. Monad m => [x] -> Producer m x () fromList l = Producer (putList l) -- | Consumer 'toStdOut' copies the given source into the standard output. toStdOut :: Consumer IO Char ()-toStdOut = Consumer $- \source-> let c = get source- >>= maybe (return ()) (\x-> lift (putChar x) >> c)- in c+toStdOut = Consumer (mapMStream_ (\x-> lift (putChar x))) -- | Producer 'fromStdIn' feeds the given sink from the standard input. fromStdIn :: Producer IO Char ()-fromStdIn = Producer $- \sink-> let p = do readyInput <- liftM not (lift isEOF)- readyOutput <- canPut sink- when (readyInput && readyOutput) (lift getChar- >>= put sink- >> p)- in p+fromStdIn = Producer (unmapMStream_ (lift isEOF >>= cond (return Nothing) (lift (liftM Just getChar)))) -- | Producer 'fromFile' opens the named file and feeds the given sink from its contents. fromFile :: String -> Producer IO Char ()@@ -102,14 +93,10 @@ -- | Producer 'fromHandle' feeds the given sink from the open file /handle/. The argument /doClose/ determines -- | if /handle/ should be closed when the handle is consumed or the sink closed. fromHandle :: Handle -> Bool -> Producer IO Char ()-fromHandle handle doClose = Producer $- \sink-> (canPut sink- >>= flip when (let p = do eof <- lift (hIsEOF handle)- when (not eof) (lift (hGetChar handle)- >>= put sink- >>= flip when p)- in p)- >> when doClose (lift $ hClose handle))+fromHandle handle doClose = Producer (\sink-> unmapMStream_ (lift hGetCharMaybe) sink+ >> when doClose (lift $ hClose handle))+ where hGetCharMaybe = hIsEOF handle >>= cond (return Nothing) (liftM Just $ hGetChar handle)+ -- | Consumer 'toFile' opens the named file and copies the given source into it. toFile :: String -> Consumer IO Char ()@@ -124,16 +111,8 @@ -- | Consumer 'toHandle' copies the given source into the open file /handle/. The argument /doClose/ determines -- | if /handle/ should be closed once the entire source is consumed and copied. toHandle :: Handle -> Bool -> Consumer IO Char ()-toHandle handle doClose = Consumer $- \source-> let c = get source- >>= maybe- (when doClose $ lift $ hClose handle)- (\x-> lift (hPutChar handle x) >> c)- in c---- | Transducer 'asis' passes its input through unmodified.-asis :: forall m x. Monad m => Transducer m x x-asis = oneToOneTransducer id+toHandle handle doClose = Consumer (\source-> mapMStream_ (lift . hPutChar handle) source+ >> when doClose (lift $ hClose handle)) -- | Transducer 'unparse' removes all markup from its input and passes the content through. unparse :: forall m x y. Monad m => Transducer m (Markup y x) x@@ -147,12 +126,11 @@ -- | The 'suppress' consumer suppresses all input it receives. It is equivalent to 'substitute' [] suppress :: forall m x y. Monad m => Consumer m x ()-suppress = Consumer consumeAndSuppress+suppress = Consumer (mapMStream_ (const $ return ())) -- | The 'erroneous' consumer reports an error if any input reaches it. erroneous :: forall m x. Monad m => String -> Consumer m x ()-erroneous message = Consumer $- \source-> get source >>= maybe (return ()) (const (error message))+erroneous message = Consumer (getWith (const (error message))) -- | The 'lowercase' transforms all uppercase letters in the input to lowercase, leaving the rest unchanged. lowercase :: forall m. Monad m => Transducer m Char Char@@ -164,7 +142,7 @@ -- | The 'count' transducer counts all its input values and outputs the final tally. count :: forall m x. Monad m => Transducer m x Integer-count = foldingTransducer (\count _-> succ count) 0 id+count = Transducer (\source sink-> foldStream (\count _-> succ count) 0 source >>= put sink) -- | Converts each input value @x@ to @show x@. toString :: forall m x. (Monad m, Show x) => Transducer m x String@@ -172,7 +150,7 @@ -- | Transducer 'group' collects all its input values into a single list. group :: forall m x. Monad m => Transducer m x [x]-group = foldingTransducer (|>) Seq.empty Foldable.toList+group = Transducer (\source sink-> foldStream (|>) Seq.empty source >>= put sink . Foldable.toList) -- | Transducer 'concatenate' flattens the input stream of lists of values into the output stream of values. concatenate :: forall m x. Monad m => Transducer m [x] x@@ -181,7 +159,7 @@ -- | Same as 'concatenate' except it inserts the given separator list between every two input lists. concatSeparate :: forall m x. Monad m => [x] -> Transducer m [x] x concatSeparate separator = statefulTransducer (\seen list-> (True, if seen then separator ++ list else list))- False + False -- | Splitter 'whitespace' feeds all white-space characters into its /true/ sink, all others into /false/. whitespace :: forall m. Monad m => Splitter m Char ()@@ -204,59 +182,29 @@ -- line-end can be formed by any of the character sequences \"\\n\", \"\\r\", \"\\r\\n\", or \"\\n\\r\". line :: forall m. Monad m => Splitter m Char () line = Splitter $- \source true false boundaries-> let split0 = get source >>= maybe (return []) split1- split1 x = if x == '\n' || x == '\r'- then split2 x- else lineChar x- split2 x = put false x- >>= cond- (get source- >>= maybe- (return [])- (\y-> if x == y- then emptyLine x- else if y == '\n' || y == '\r'- then split3 x- else lineChar y))- (return [x])- split3 x = put false x- >>= cond- (get source- >>= maybe- (return [])- (\y-> if y == '\n' || y == '\r'- then emptyLine y- else lineChar y))- (return [x])- emptyLine x = put boundaries () >>= cond (split2 x) (return [])- lineChar x = put true x >>= cond split0 (return [x])- in split0+ \source true false boundaries-> let split Nothing x = put boundaries () >> handle x+ split (Just '\n') x@'\r' = put false x >> return Nothing+ split (Just '\r') x@'\n' = put false x >> return Nothing+ split (Just '\n') x = split Nothing x+ split (Just '\r') x = split Nothing x+ split (Just _) x = handle x+ handle x = (if x == '\n' || x == '\r'+ then put false x+ else put true x)+ >> return (Just x)+ in foldMStream_ split Nothing source -- | Splitter 'everything' feeds its entire input into its /true/ sink. everything :: forall m x. Monad m => Splitter m x ()-everything = Splitter $- \source true false edge-> do put edge ()- pour source true- return []+everything = Splitter (\source true false edge-> put edge () >> pour source true) -- | Splitter 'nothing' feeds its entire input into its /false/ sink. nothing :: forall m x. Monad m => Splitter m x ()-nothing = Splitter $- \source true false edge-> do pour source false- return []+nothing = Splitter (\source true false edge-> pour source false) -- | Splitter 'one' feeds all input values to its /true/ sink, treating every value as a separate section. one :: forall m x. Monad m => Splitter m x ()-one = Splitter $- \source true false edge-> let s = get source- >>= maybe- (return [])- (\x-> put edge ()- >>= cond- (put true x- >>= cond s (return [x]))- (return [x]))- in s+one = Splitter (\source true false edge-> mapMStream_ (\x-> put edge () >> put true x) source) -- | Splitter 'marked' passes all marked-up input sections to its /true/ sink, and all unmarked input to its -- /false/ sink.@@ -305,12 +253,9 @@ -- | Performs the same task as the 'substring' splitter, but instead of splitting it outputs the input as @'Markup' x -- 'OccurenceTag'@ in order to distinguish overlapping strings. parseSubstring :: forall m x y. (Monad m, Eq x) => [x] -> Parser m x OccurenceTag-parseSubstring [] = Transducer $- \ source sink -> let next = get source- >>= maybe (return []) wrap- wrap x = put sink (Content x) >>= cond prepend (return [x])- prepend = put sink (Markup (Point (toEnum 1))) >>= cond next (return [])- in prepend+parseSubstring [] = Transducer $ \ source sink ->+ put sink marker >> mapMStream_ (\x-> put sink (Content x) >> put sink marker) source+ where marker = Markup (Point (toEnum 1)) parseSubstring list = Transducer $ \ source sink ->@@ -324,30 +269,21 @@ in if x == head then if null tail then put sink (Markup (Start (toEnum id')))- >>= cond- (put sink qh- >>= cond- (fallback id' (qt- |> Markup (End (toEnum id'))))- (return $ remainingContent q'))- (return $ remainingContent q')+ >> put sink qh+ >> (fallback id' (qt |> Markup (End (toEnum id')))) else getNext id tail q' else fallback id q' fallback id q = case Seq.viewl q of EmptyL -> getNext id list q head@(Markup (End id')) :< tail -> put sink head- >>= cond- (fallback- (if id == fromEnum id' then 0 else id)- tail)- (return $ remainingContent tail)+ >> fallback+ (if id == fromEnum id' then 0 else id)+ tail view@(head@Content{} :< tail) -> case stripPrefix (remainingContent q) list of Just rest -> getNext id rest q Nothing -> put sink head- >>= cond- (fallback id tail)- (return $ remainingContent q)- flush q = liftM extractContent $ putList (Foldable.toList $ Seq.viewl q) sink+ >> fallback id tail+ flush q = putQueue q sink remainingContent :: Seq (Markup OccurenceTag x) -> [x] remainingContent q = extractContent (Seq.viewl q) extractContent :: Foldable.Foldable f => f (Markup b x) -> [x]@@ -358,10 +294,7 @@ -- argument. If two overlapping parts of the input both match the argument, both are sent to /true/ and each is preceded -- by an edge. substring :: forall m x. (Monad m, Eq x) => [x] -> Splitter m x ()-substring [] = Splitter $- \ source true false edge -> do rest <- split one source false true edge- put edge ()- return rest+substring [] = Splitter $ \ source true false edge -> split one source false true edge >> put edge () substring list = Splitter $ \ source true false edge ->@@ -376,9 +309,7 @@ then if null tail then put edge () >> put true qqh- >>= cond- (fallback qqt Seq.empty)- (return $ Foldable.toList view)+ >> fallback qqt Seq.empty else getNext tail qt qf' else fallback qt qf' fallback qt qf = case Seq.viewl (qt >< qf)@@ -387,11 +318,7 @@ of Just rest -> getNext rest qt qf Nothing -> if Seq.null qt then put false head- >>= cond- (fallback Seq.empty tail)- (return $ Foldable.toList view)+ >> fallback Seq.empty tail else put true head- >>= cond- (fallback (Seq.drop 1 qt) qf)- (return $ Foldable.toList view)+ >> fallback (Seq.drop 1 qt) qf in getNext list Seq.empty Seq.empty
Control/Concurrent/SCC/Streams.hs view
@@ -15,12 +15,12 @@ -} -- | This module defines 'Source' and 'Sink' types and 'pipe' functions that create them. The method 'get' on 'Source'--- abstracts away 'Control.Concurrent.SCC.Coroutine.await', and the method 'put' on 'Sink' is a higher-level--- abstraction of 'Control.Concurrent.SCC.Coroutine.yield'. With this arrangement, a single coroutine can yield values+-- abstracts away 'Control.Concurrent.Coroutine.await', and the method 'put' on 'Sink' is a higher-level abstraction of+-- 'Control.Concurrent.Coroutine.SuspensionFunctors.yield'. With this arrangement, a single coroutine can yield values -- to multiple sinks and await values from multiple sources with no need to change the--- 'Control.Concurrent.SCC.Coroutine.Coroutine' functor; the only requirement is for each funtor of the sources and--- sinks the coroutine uses to be an 'Control.Concurrent.SCC.Coroutine.AncestorFunctor' of the coroutine's--- functor. For example, coroutine /zip/ that takes two sources and one sink would be declared like this:+-- 'Control.Concurrent.Coroutine.Coroutine' functor; the only requirement is for each funtor of the sources and sinks+-- the coroutine uses to be an 'Control.Concurrent.Coroutine.AncestorFunctor' of the coroutine's functor. For example,+-- coroutine /zip/ that takes two sources and one sink would be declared like this: -- -- @ -- zip :: forall m a1 a2 a3 d x y. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d)@@ -37,8 +37,8 @@ -- add :: forall m a1 a2 a3 d. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d) -- => Source m a1 Integer -> Source m a2 Integer -> Sink m a3 Integer -> Coroutine d m () -- add source1 source2 sink = do pipe--- (\pairSink-> zip source1 source2 pairSink) -- producer coroutine--- (\pairSource-> pourMap (uncurry (+)) pairSource sink) -- consumer coroutine+-- (\pairSink-> zip source1 source2 pairSink) -- producer coroutine+-- (\pairSource-> mapStream (uncurry (+)) pairSource sink) -- consumer coroutine -- return () -- @ @@ -47,64 +47,76 @@ module Control.Concurrent.SCC.Streams ( -- * Sink and Source types- Sink(put, canPut), Source(get),- SinkFunctor, SourceFunctor,- -- * Various pipe functions- pipe, pipeP, pipePS,- -- * Utility functions- get', getSuccess,+ Sink, Source, SinkFunctor, SourceFunctor, AncestorFunctor,+ -- * Sink and Source constructors+ pipe, pipeP, pipePS, nullSink, nullSource,+ -- * Operations on sinks and sources+ -- ** Singleton operations+ get, put, getWith,+ -- ** Lifting functions liftSink, liftSource,- consumeAndSuppress, tee, pour, pourMap, getList, putList, putQueue,- cond, whenNull+ -- ** Bulk operations+ pour, tee, teeSink, teeSource,+ mapStream, mapSource, mapSink, mapMStream, mapMSource, mapMSink, mapMStream_,+ mapMaybeStream, mapMaybeSink, mapMaybeSource,+ filterMStream, filterMSource, filterMSink,+ foldStream, foldMStream, foldMStream_, mapAccumStream, partitionStream,+ unfoldMStream, unmapMStream_,+ zipWithMStream, parZipWithMStream,+ getList, putList, putQueue,+ -- * Utility functions+ cond ) where -import Control.Concurrent.Coroutine+import qualified Control.Monad+import qualified Data.List+import qualified Data.Maybe -import Control.Monad (when)+import Control.Monad (liftM, when) import Data.Foldable (toList) import Data.Sequence (Seq, viewl) -type TryYield x = EitherFunctor (Yield x) (Await Bool)--tryYield :: forall m x. Monad m => x -> Coroutine (TryYield x) m Bool-tryYield x = suspend (LeftF (Yield x (suspend (RightF (Await return)))))--canYield :: forall m x. Monad m => Coroutine (TryYield x) m Bool-canYield = suspend (RightF (Await return))+import Control.Monad.Parallel (MonadParallel(..))+import Control.Monad.Coroutine+import Control.Monad.Coroutine.SuspensionFunctors (Await(Await), Yield(Yield), EitherFunctor(..), await, yield)+import Control.Monad.Coroutine.Nested (AncestorFunctor(..), liftOut, seesawNested) type SourceFunctor a x = EitherFunctor a (Await (Maybe x))-type SinkFunctor a x = EitherFunctor a (TryYield x)+type SinkFunctor a x = EitherFunctor a (Yield x) -- | A 'Sink' can be used to yield values from any nested `Coroutine` 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 =+-- the functor /a/. It's the write-only end of a communication channel created by 'pipe'.+newtype Sink (m :: * -> *) a x = Sink {- -- | Function 'put' tries to put a value into the given `Sink`. The intervening 'Coroutine' 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 -> Coroutine 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) => Coroutine d m Bool+ -- | This function puts a value into the given `Sink`. The intervening 'Coroutine' computations suspend up+ -- to the 'pipe' invocation that has created the argument sink.+ put :: forall d. AncestorFunctor a d => x -> Coroutine d m () } -- | A 'Source' can be used to read values into any nested `Coroutine` computation whose functor provably descends from--- the functor /a/. It's the read-only end of a 'Pipe' communication channel.+-- the functor /a/. It's the read-only end of a communication channel created by 'pipe'. newtype Source (m :: * -> *) a x = Source { -- | Function 'get' tries to get a value from the given 'Source' argument. The intervening 'Coroutine' 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) => Coroutine d m (Maybe x)+ get :: forall d. AncestorFunctor a d => Coroutine d m (Maybe x) } +-- | A disconnected sink that ignores all values 'put' into it.+nullSink :: forall m a x. Monad m => Sink m a x+nullSink = Sink{put= const (return ())}++-- | An empty source whose 'get' always returns Nothing.+nullSource :: forall m a x. Monad m => Source m a x+nullSource = Source{get= return Nothing}+ -- | 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 -> Coroutine d m Bool),- canPut= liftOut (canPut s :: Coroutine d m Bool)}+liftSink s = Sink {put= liftOut . (put s :: x -> Coroutine d m ())} -- | 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@@ -117,13 +129,13 @@ (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2) pipe = pipeG (\ f mx my -> do {x <- mx; y <- my; f x y}) --- | 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) =>+-- | The 'pipeP' function is equivalent to 'pipe', except it runs the /producer/ and the /consumer/ in parallel.+pipeP :: forall m a a1 a2 x r1 r2. (MonadParallel m, Functor a, a1 ~ SinkFunctor a x, a2 ~ SourceFunctor a x) => (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2) pipeP = pipeG bindM2 -- | 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) =>+pipePS :: forall m a a1 a2 x r1 r2. (MonadParallel m, Functor a, a1 ~ SinkFunctor a x, a2 ~ SourceFunctor a x) => Bool -> (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2) pipePS parallel = if parallel then pipeP else pipe@@ -134,81 +146,211 @@ -> (Sink m a1 x -> Coroutine a1 m r1) -> (Source m a2 x -> Coroutine a2 m r2) -> Coroutine a m (r1, r2) pipeG run2 producer consumer =- seesawNested run2 resolver (producer sink) (consumer source)- where sink = Sink {put= liftOut . (local . tryYield :: x -> Coroutine a1 m Bool),- canPut= liftOut (local canYield :: Coroutine a1 m Bool)} :: Sink m a1 x- source = Source (liftOut (local await :: Coroutine a2 m (Maybe x))) :: Source m a2 x+ liftM (uncurry (flip (,))) $ seesawNested run2 resolver (consumer source) (producer sink)+ where sink = Sink {put= liftOut . (mapSuspension RightF . yield :: x -> Coroutine a1 m ())} :: Sink m a1 x+ source = Source (liftOut (mapSuspension RightF await :: Coroutine a2 m (Maybe x))) :: Source m a2 x resolver = SeesawResolver {- resumeLeft= \s-> case s of (LeftF (Yield _ c))-> c- (RightF (Await c))-> c False,- resumeRight = \(Await c)-> c Nothing,- resumeAny= \ resumeProducer _ resumeBoth s (Await cc) ->- case s of LeftF (Yield x cp) -> resumeBoth cp (cc (Just x))- RightF (Await cp) -> resumeProducer (cp True)+ resumeLeft = \(Await c)-> c Nothing,+ resumeRight= \(Yield _ c)-> c,+ resumeAny= \ _ resumeProducer resumeBoth (Await cc) (Yield x cp) -> resumeBoth (cc (Just x)) cp } -getSuccess :: forall m a d x . (Monad m, AncestorFunctor a d)- => Source m a x -> (x -> Coroutine d m ()) {- ^ Success continuation -} -> Coroutine 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 -> Coroutine d m x-get' source = get source >>= maybe (error "get' failed") return+-- | Invokes its first argument with the value it gets from the source, if there is any to get.+getWith :: forall m a d x. (Monad m, AncestorFunctor a d) => (x -> Coroutine d m ()) -> Source m a x -> Coroutine d m ()+getWith consumer source = get source >>= maybe (return ()) consumer --- | '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' copies all data from the /source/ argument into the /sink/ argument. pour :: forall m a1 a2 d x . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => Source m a1 x -> Sink m a2 x -> Coroutine d m ()-pour source sink = fill'- where fill' = canPut sink >>= flip when (getSuccess source (\x-> put sink x >> fill'))+pour source sink = mapMStream_ (put sink) source --- | '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)+-- | 'mapStream' is like 'pour' that applies the function /f/ to each argument before passing it into the /sink/.+mapStream :: 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 -> Coroutine d m ()-pourMap f source sink = loop- where loop = canPut sink >>= flip when (get source >>= maybe (return ()) (\x-> put sink (f x) >> loop))+mapStream f source sink = mapMStream_ (put sink . f) source --- | '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)+-- | An equivalent of 'Data.List.map' that works on a 'Source' instead of a list. The argument function is applied to+-- every value after it's read from the source argument.+mapSource :: forall m a x y. Monad m => (x -> y) -> Source m a x -> Source m a y+mapSource f source = Source{get= liftM (fmap f) (get source)}++-- | An equivalent of 'Data.List.map' that works on a 'Sink' instead of a list. The argument function is applied to+-- every value vefore it's written to the sink argument.+mapSink :: forall m a x y. Monad m => (x -> y) -> Sink m a y -> Sink m a x+mapSink f sink = Sink{put= put sink . f}++-- | 'mapMaybeStream' is to 'mapStream' like 'Data.Maybe.mapMaybe' is to 'Data.List.map'.+mapMaybeStream :: 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 -> Coroutine 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))+mapMaybeStream f source sink = mapMStream_ (maybe (return ()) (put sink) . f) source --- | 'tee' is similar to 'pour' except it distributes every input value from the /source/ arguments into both /sink1/--- and /sink2/.+-- | 'mapMaybeSink' is to 'mapSink' like 'Data.Maybe.mapMaybe' is to 'Data.List.map'.+mapMaybeSink :: forall m a x y . Monad m => (x -> Maybe y) -> Sink m a y -> Sink m a x+mapMaybeSink f sink = Sink{put= maybe (return ()) (put sink) . f}++-- | 'mapMaybeSource' is to 'mapSource' like 'Data.Maybe.mapMaybe' is to 'Data.List.map'.+mapMaybeSource :: forall m a x y . Monad m => (x -> Maybe y) -> Source m a x -> Source m a y+mapMaybeSource f source = Source{get= next}+ where next :: forall d. AncestorFunctor a d => Coroutine d m (Maybe y)+ next = get source+ >>= maybe (return Nothing) (maybe next (return . Just) . f)++-- | 'mapMStream' is similar to 'Control.Monad.mapM'. It draws the values from a 'Source' instead of a list, writes the+-- mapped values to a 'Sink', and returns a 'Coroutine'.+mapMStream :: forall m a1 a2 d x y . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d)+ => (x -> Coroutine d m y) -> Source m a1 x -> Sink m a2 y -> Coroutine d m ()+mapMStream f source sink = loop+ where loop = getWith (\x-> f x >>= put sink >> loop) source++-- | An equivalent of 'Control.Monad.mapM' that works on a 'Source' instead of a list. Similar to 'mapSource', except+-- the function argument is monadic and may have perform effects.+mapMSource :: forall m a x y. Monad m+ => (forall d. AncestorFunctor a d => x -> Coroutine d m y) -> Source m a x -> Source m a y+mapMSource f source = Source{get= get source >>= maybe (return Nothing) (liftM Just . f)}++-- | An equivalent of 'Control.Monad.mapM' that works on a 'Sink' instead of a list. Similar to 'mapSink', except the+-- function argument is monadic and may have perform effects.+mapMSink :: forall m a x y. Monad m+ => (forall d. AncestorFunctor a d => x -> Coroutine d m y) -> Sink m a y -> Sink m a x+mapMSink f sink = Sink{put= (put sink =<<) . f}++-- | 'mapMStream_' is similar to 'Control.Monad.mapM_' except it draws the values from a 'Source' instead of a list and+-- works with 'Coroutine' instead of an arbitrary monad.+mapMStream_ :: forall m a d x . (Monad m, AncestorFunctor a d)+ => (x -> Coroutine d m ()) -> Source m a x -> Coroutine d m ()+mapMStream_ f source = loop+ where loop = getWith (\x-> f x >> loop) source++-- | An equivalent of 'Control.Monad.filterM'. Draws the values from a 'Source' instead of a list, writes the filtered+-- values to a 'Sink', and returns a 'Coroutine'.+filterMStream :: forall m a1 a2 d x . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d)+ => (x -> Coroutine d m Bool) -> Source m a1 x -> Sink m a2 x -> Coroutine d m ()+filterMStream f source sink = mapMStream_ (\x-> f x >>= cond (put sink x) (return ())) source++-- | An equivalent of 'Control.Monad.filterM'; filters a 'Source' instead of a list.+filterMSource :: forall m a x y . Monad m+ => (forall d. AncestorFunctor a d => x -> Coroutine d m Bool) -> Source m a x -> Source m a x+filterMSource f source = Source{get= find}+ where find :: forall d. AncestorFunctor a d => Coroutine d m (Maybe x)+ find = get source >>= maybe (return Nothing) (\x-> f x >>= cond (return (Just x)) find)++-- | An equivalent of 'Control.Monad.filterM'; filters a 'Sink' instead of a list.+filterMSink :: forall m a x y . Monad m+ => (forall d. AncestorFunctor a d => x -> Coroutine d m Bool) -> Sink m a x -> Sink m a x+filterMSink f sink = Sink{put= \x-> f x >>= cond (put sink x) (return ())}++-- | Similar to 'Data.List.foldl', but reads the values from a 'Source' instead of a list.+foldStream :: forall m a d x acc . (Monad m, AncestorFunctor a d)+ => (acc -> x -> acc) -> acc -> Source m a x -> Coroutine d m acc+foldStream f s source = loop s+ where loop s = get source >>= maybe (return s) (\x-> loop (f s x))++-- | 'foldMStream' is similar to 'Control.Monad.foldM' except it draws the values from a 'Source' instead of a list and+-- works with 'Coroutine' instead of an arbitrary monad.+foldMStream :: forall m a d x acc . (Monad m, AncestorFunctor a d)+ => (acc -> x -> Coroutine d m acc) -> acc -> Source m a x -> Coroutine d m acc+foldMStream f acc source = loop acc+ where loop acc = get source >>= maybe (return acc) (\x-> f acc x >>= loop)++-- | A version of 'foldMStream' that ignores the final result value.+foldMStream_ :: forall m a d x acc . (Monad m, AncestorFunctor a d)+ => (acc -> x -> Coroutine d m acc) -> acc -> Source m a x -> Coroutine d m ()+foldMStream_ f acc source = loop acc+ where loop acc = getWith (\x-> f acc x >>= loop) source++-- | 'unfoldMStream' is a version of 'Data.List.unfoldr' that writes the generated values into a 'Sink' instead of+-- returning a list.+unfoldMStream :: forall m a d x acc . (Monad m, AncestorFunctor a d)+ => (acc -> Coroutine d m (Maybe (x, acc))) -> acc -> Sink m a x -> Coroutine d m acc+unfoldMStream f acc sink = loop acc+ where loop acc = f acc >>= maybe (return acc) (\(x, acc')-> put sink x >> loop acc')++-- | 'unmapMStream_' is opposite of 'mapMStream_'; it takes a 'Sink' instead of a 'Source' argument and writes the+-- generated values into it.+unmapMStream_ :: forall m a d x . (Monad m, AncestorFunctor a d)+ => Coroutine d m (Maybe x) -> Sink m a x -> Coroutine d m ()+unmapMStream_ f sink = loop+ where loop = f >>= maybe (return ()) (\x-> put sink x >> loop)++-- | 'mapAccumStream' is similar to 'Data.List.mapAccumL' except it reads the values from a 'Source' instead of a list+-- and writes the mapped values into a 'Sink' instead of returning another list.+mapAccumStream :: forall m a1 a2 d x y acc . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d)+ => (acc -> x -> (acc, y)) -> acc -> Source m a1 x -> Sink m a2 y -> Coroutine d m acc+mapAccumStream f acc source sink = loop acc+ where loop acc = get source >>= maybe (return acc) (\x-> let (acc', y) = f acc x in put sink y >> loop acc')++-- | Equivalent to 'Data.List.partition'. Takes a 'Source' instead of a list argument and partitions its contents into+-- the two 'Sink' arguments.+partitionStream :: forall m a1 a2 a3 d x . (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d)+ => (x -> Bool) -> Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Coroutine d m ()+partitionStream f source true false = mapMStream_ (\x-> if f x then put true x else put false x) source++-- | 'zipWithMStream' is similar to 'Control.Monad.zipWithM' except it draws the values from two 'Source' arguments+-- instead of two lists, sends the results into a 'Sink', and works with 'Coroutine' instead of an arbitrary monad.+zipWithMStream :: forall m a1 a2 a3 d x y z. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d)+ => (x -> y -> Coroutine d m z) -> Source m a1 x -> Source m a2 y -> Sink m a3 z -> Coroutine d m ()+zipWithMStream f source1 source2 sink = loop+ where loop = do mx <- get source1+ my <- get source2+ case (mx, my) of (Just x, Just y) -> f x y >>= put sink >> loop+ _ -> return ()++-- | 'parZipWithMStream' is equivalent to 'zipWithMStream', but it consumes the two sources in parallel.+parZipWithMStream :: forall m a1 a2 a3 d x y z.+ (MonadParallel m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d)+ => (x -> y -> Coroutine d m z) -> Source m a1 x -> Source m a2 y -> Sink m a3 z -> Coroutine d m ()+parZipWithMStream f source1 source2 sink = loop+ where loop = bindM2 zip (get source1) (get source2)+ zip (Just x) (Just y) = f x y >>= put sink >> loop+ zip _ _ = return ()++-- | 'tee' is similar to 'pour' except it distributes every input value from its source argument into its both sink+-- arguments. 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 -> Coroutine 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))+ where distribute = 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 -> Coroutine d m [x]-putList [] sink = return []-putList l@(x:rest) sink = put sink x >>= cond (putList rest sink) (return l)+-- | Every value 'put' into a 'teeSink' result sink goes into its both argument sinks: @put (teeSink s1 s2) x@ is+-- equivalent to @put s1 x >> put s2 x@.+teeSink :: forall m a1 a2 a3 x . (Monad m, AncestorFunctor a1 a3, AncestorFunctor a2 a3)+ => Sink m a1 x -> Sink m a2 x -> Sink m a3 x+teeSink s1 s2 = Sink{put= tee}+ where tee :: forall d. AncestorFunctor a3 d => x -> Coroutine d m ()+ tee x = put s1' x >> put s2' x+ s1' :: Sink m a3 x+ s1' = liftSink s1+ s2' :: Sink m a3 x+ s2' = liftSink s2 +-- | The 'Source' returned by 'teeSource' writes every value read from its argument source into the argument sink before+-- providing it back.+teeSource :: forall m a1 a2 a3 x . (Monad m, AncestorFunctor a1 a3, AncestorFunctor a2 a3)+ => Sink m a1 x -> Source m a2 x -> Source m a3 x+teeSource sink source = Source{get= tee}+ where tee :: forall d. AncestorFunctor a3 d => Coroutine d m (Maybe x)+ tee = do mx <- get source'+ maybe (return ()) (put sink') mx+ return mx+ sink' :: Sink m a3 x+ sink' = liftSink sink+ source' :: Source m a3 x+ source' = liftSource source++-- | 'putList' puts entire list into its /sink/ argument.+putList :: forall m a d x. (Monad m, AncestorFunctor a d) => [x] -> Sink m a x -> Coroutine d m ()+putList [] sink = return ()+putList l@(x:rest) sink = put sink x >> putList rest sink+ -- | '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 -> Coroutine 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 -> Coroutine 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 -> Coroutine d m [x]+putQueue :: forall m a d x. (Monad m, AncestorFunctor a d) => Seq x -> Sink m a x -> Coroutine d m () putQueue q sink = putList (toList (viewl q)) sink
Control/Concurrent/SCC/Types.hs view
@@ -14,7 +14,7 @@ <http://www.gnu.org/licenses/>. -} --- | This module defines various 'Control.Concurrent.SCC.Coroutine.Coroutine' types that operate on+-- | This module defines various 'Control.Concurrent.SCC.Coroutine' types that operate on -- 'Control.Concurrent.SCC.Streams.Sink' and 'Control.Concurrent.SCC.Streams.Source' values. The simplest of the bunch -- are 'Consumer' and 'Producer' types, which respectively operate on a single source or sink. A 'Transducer' has access -- both to a 'Control.Concurrent.SCC.Streams.Source' to read from and a 'Control.Concurrent.SCC.Streams.Sink' to write@@ -35,55 +35,56 @@ Branching (combineBranches), -- * Constructors isolateConsumer, isolateProducer, isolateTransducer, isolateSplitter,- oneToOneTransducer, statelessTransducer, foldingTransducer, statefulTransducer,+ oneToOneTransducer, statelessTransducer, statefulTransducer, statelessSplitter, statefulSplitter, -- * Utility functions- splitToConsumers, splitInputToConsumers, pipePS+ splitToConsumers, splitInputToConsumers, pipePS, (>|>), (<|<) ) where -import Control.Concurrent.Coroutine+import Control.Monad.Coroutine+import Control.Monad.Parallel (MonadParallel(..))+ import Control.Concurrent.SCC.Streams +import Control.Category (Category(..)) import Control.Monad (liftM, when) import Data.Maybe (maybe) type OpenConsumer m a d x r = AncestorFunctor a d => Source m a x -> Coroutine d m r type OpenProducer m a d x r = AncestorFunctor a d => Sink m a x -> Coroutine d m r-type OpenTransducer m a1 a2 d x y = - (AncestorFunctor a1 d, AncestorFunctor a2 d) => Source m a1 x -> Sink m a2 y -> Coroutine d m [x]-type OpenSplitter m a1 a2 a3 a4 d x b =+type OpenTransducer m a1 a2 d x y r = + (AncestorFunctor a1 d, AncestorFunctor a2 d) => Source m a1 x -> Sink m a2 y -> Coroutine d m r+type OpenSplitter m a1 a2 a3 a4 d x b r = (AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d, AncestorFunctor a4 d) =>- Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Sink m a4 b -> Coroutine d m [x]+ Source m a1 x -> Sink m a2 x -> Sink m a3 x -> Sink m a4 b -> Coroutine d m r --- | A component that performs a computation with no inputs nor outputs.+-- | A coroutine that has no inputs nor outputs - and therefore may not suspend at all, which means it's not really a+-- /co/routine. newtype Performer m r = Performer {perform :: m r} --- | A component that consumes values from a 'Control.Concurrent.SCC.Streams.Source'.+-- | A coroutine that consumes values from a 'Control.Concurrent.SCC.Streams.Source'. newtype Consumer m x r = Consumer {consume :: forall a d. OpenConsumer m a d x r} --- | A component that produces values and puts them into a 'Control.Concurrent.SCC.Streams.Sink'.+-- | A coroutine that produces values and puts them into a 'Control.Concurrent.SCC.Streams.Sink'. newtype Producer m x r = Producer {produce :: forall a d. OpenProducer m a d x r} --- | The 'Transducer' type represents computations that transform a data stream. Execution of 'transduce' must continue+-- | The 'Transducer' type represents coroutines that transform a data stream. Execution of 'transduce' must continue -- consuming the given 'Control.Concurrent.SCC.Streams.Source' and feeding the 'Control.Concurrent.SCC.Streams.Sink' as--- long both can be resumed. If the sink dies first, 'transduce' should return the list of all values it has consumed--- from the source but hasn't managed to process and write into the sink.-newtype Transducer m x y = Transducer {transduce :: forall a1 a2 d. OpenTransducer m a1 a2 d x y}+-- long as there is any data in the source.+newtype Transducer m x y = Transducer {transduce :: forall a1 a2 d. OpenTransducer m a1 a2 d x y ()} --- | The 'SplitterComponent' type represents computations that distribute the input stream acording to some criteria. A--- splitter should distribute only the original input data, and feed it into the sinks in the same order it has been--- read from the source. Furthermore, the input source should be entirely consumed and fed into the first two sinks. The--- third sink can be used to supply extra information at arbitrary points in the input. If any of the sinks dies before--- all data is fed to them, 'split' should return the list of all values it has consumed from the source but hasn't--- managed to write into the sinks.+-- | The 'Splitter' type represents coroutines that distribute the input stream acording to some criteria. A splitter+-- should distribute only the original input data, and feed it into the sinks in the same order it has been read from+-- the source. Furthermore, the input source should be entirely consumed and fed into the first two sinks. The third+-- sink can be used to supply extra information at arbitrary points in the input. -- -- A splitter can be used in two ways: as a predicate to determine which portions of its input stream satisfy a certain -- property, or as a chunker to divide the input stream into chunks. In the former case, the predicate is considered -- true for exactly those parts of the input that are written to its /true/ sink. In the latter case, a chunk is a -- contiguous section of the input stream that is written exclusively to one sink, either true or false. Anything -- written to the third sink also terminates the chunk.-newtype Splitter m x b = Splitter {split :: forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b}+newtype Splitter m x b = Splitter {split :: forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b ()} -- | A 'Markup' value is produced to mark either a 'Start' and 'End' of a region of data, or an arbitrary -- 'Point' in data. A 'Point' is semantically equivalent to a 'Start' immediately followed by 'End'. The 'Content'@@ -105,6 +106,24 @@ showsPrec p (Content x) s = x : s showsPrec p (Markup b) s = '[' : shows b (']' : s) +instance Monad m => Category (Transducer m) where+ id = Transducer pour+ t1 . t2 = isolateTransducer $ \source sink-> + pipe (transduce t2 source) (\source-> transduce t1 source sink)+ >> return ()++-- | Same as 'Control.Category.>>>' except it runs the two transducers in parallel.+(>|>) :: MonadParallel m => Transducer m x y -> Transducer m y z -> Transducer m x z+t1 >|> t2 = isolateTransducer $ \source sink-> + pipeP (transduce t1 source) (\source-> transduce t2 source sink)+ >> return ()++-- | Same as 'Control.Category.<<<' except it runs the two transducers in parallel.+(<|<) :: MonadParallel m => Transducer m y z -> Transducer m x y -> Transducer m x z+t1 <|< t2 = isolateTransducer $ \source sink-> + pipeP (transduce t2 source) (\source-> transduce t1 source sink)+ >> return ()+ -- | Creates a proper 'Consumer' from a function that is, but can't be proven to be, an 'OpenConsumer'. isolateConsumer :: forall m x r. Monad m => (forall d. Functor d => Source m d x -> Coroutine d m r) -> Consumer m x r isolateConsumer consume = Consumer consume'@@ -123,9 +142,9 @@ -- | Creates a proper 'Transducer' from a function that is, but can't be proven to be, an 'OpenTransducer'. isolateTransducer :: forall m x y. Monad m => - (forall d. Functor d => Source m d x -> Sink m d y -> Coroutine d m [x]) -> Transducer m x y+ (forall d. Functor d => Source m d x -> Sink m d y -> Coroutine d m ()) -> Transducer m x y isolateTransducer transduce = Transducer transduce'- where transduce' :: forall a1 a2 d. OpenTransducer m a1 a2 d x y+ where transduce' :: forall a1 a2 d. OpenTransducer m a1 a2 d x y () transduce' source sink = let source' :: Source m d x source' = liftSource source sink' :: Sink m d y@@ -135,10 +154,10 @@ -- | Creates a proper 'Splitter' from a function that is, but can't be proven to be, an 'OpenSplitter'. isolateSplitter :: forall m x b. Monad m => (forall d. Functor d => - Source m d x -> Sink m d x -> Sink m d x -> Sink m d b -> Coroutine d m [x]) + Source m d x -> Sink m d x -> Sink m d x -> Sink m d b -> Coroutine d m ()) -> Splitter m x b isolateSplitter split = Splitter split'- where split' :: forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b+ where split' :: forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b () split' source true false edge = let source' :: Source m d x source' = liftSource source true' :: Sink m d x@@ -163,17 +182,9 @@ instance forall m x r. Monad m => Branching (Consumer m x r) m x r where combineBranches combinator parallel c1 c2 = Consumer $ combinator parallel (consume c1) (consume c2) -instance forall m x. Monad m => Branching (Consumer m x ()) m x [x] where- combineBranches combinator parallel c1 c2- = Consumer $- liftM (const ())- . combinator parallel- (\source-> consume c1 source >> return [])- (\source-> consume c2 source >> return [])--instance forall m x y. Monad m => Branching (Transducer m x y) m x [x] where+instance forall m x y. Monad m => Branching (Transducer m x y) m x () where combineBranches combinator parallel t1 t2- = let transduce' :: forall a1 a2 d. OpenTransducer m a1 a2 d x y+ = let transduce' :: forall a1 a2 d. OpenTransducer m a1 a2 d x y () transduce' source sink = combinator parallel (\source-> transduce t1 source sink') (\source-> transduce t2 source sink')@@ -182,9 +193,9 @@ sink' = liftSink sink in Transducer transduce' -instance forall m x b. (ParallelizableMonad m) => Branching (Splitter m x b) m x [x] where+instance forall m x b. (MonadParallel m) => Branching (Splitter m x b) m x () where combineBranches combinator parallel s1 s2- = let split' :: forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b+ = let split' :: forall a1 a2 a3 a4 d. OpenSplitter m a1 a2 a3 a4 d x b () split' source true false edge = combinator parallel (\source-> split s1 source true' false' edge') (\source-> split s2 source true' false' edge')@@ -200,62 +211,32 @@ -- | Function 'oneToOneTransducer' takes a function that maps one input value to one output value each, and lifts it -- into a 'Transducer'. oneToOneTransducer :: Monad m => (x -> y) -> Transducer m x y-oneToOneTransducer f = Transducer $- \source sink-> let t = canPut sink- >>= flip when (getSuccess source (\x-> put sink (f x) >> t))- in t >> return []+oneToOneTransducer f = Transducer (mapStream f) -- | Function 'statelessTransducer' takes a function that maps one input value into a list of output values, and -- lifts it into a 'Transducer'. statelessTransducer :: Monad m => (x -> [y]) -> Transducer m x y-statelessTransducer f = Transducer $- \source sink-> let t = canPut sink- >>= flip when (getSuccess source (\x-> putList (f x) sink >> t))- in t >> return []---- | Function 'foldingTransducer' creates a stateful transducer that produces only one output value after consuming the--- entire input. Similar to 'Data.List.foldl'-foldingTransducer :: Monad m => (s -> x -> s) -> s -> (s -> y) -> Transducer m x y-foldingTransducer f s0 w = Transducer $- \source sink-> let t s = canPut sink- >>= flip when (get source- >>= maybe- (put sink (w s) >> return ())- (t . f s))- in t s0 >> return []+statelessTransducer f = Transducer (\source sink-> mapMStream_ (\x-> putList (f x) sink) source) -- | Function 'statefulTransducer' constructs a 'Transducer' from a state-transition function and the initial -- state. The transition function may produce arbitrary output at any transition step. statefulTransducer :: Monad m => (state -> x -> (state, [y])) -> state -> Transducer m x y-statefulTransducer f s0 = Transducer $- \source sink-> let t s = canPut sink- >>= flip when (getSuccess source- (\x-> let (s', ys) = f s x- in putList ys sink >> t s'))- in t s0 >> return []+statefulTransducer f s0 = + Transducer (\source sink-> foldMStream_ (\ s x -> let (s', ys) = f s x in putList ys sink >> return s') s0 source) -- | Function 'statelessSplitter' takes a function that assigns a Boolean value to each input item and lifts it into -- a 'Splitter'. statelessSplitter :: Monad m => (x -> Bool) -> Splitter m x b-statelessSplitter f = Splitter (\source true false edge->- let s = get source- >>= maybe- (return [])- (\x-> (if f x then put true x else put false x)- >>= cond s (return [x]))- in s)+statelessSplitter f = Splitter (\source true false edge-> partitionStream f source true false) -- | Function 'statefulSplitter' takes a state-converting function that also assigns a Boolean value to each input -- item and lifts it into a 'Splitter'. statefulSplitter :: Monad m => (state -> x -> (state, Bool)) -> state -> Splitter m x ()-statefulSplitter f s0 = Splitter (\source true false edge->- let split s = get source- >>= maybe- (return [])- (\x-> let (s', truth) = f s x- in (if truth then put true x else put false x)- >>= cond (split s') (return [x]))- in split s0)+statefulSplitter f s0 = + Splitter (\source true false edge-> + foldMStream_ + (\ s x -> let (s', truth) = f s x in (if truth then put true x else put false x) >> return s')+ s0 source) -- | Given a 'Splitter', a 'Source', and three consumer functions, 'splitToConsumers' runs the splitter on the source -- and feeds the splitter's outputs to its /true/, /false/, and /edge/ sinks, respectively, to the three consumers.@@ -266,7 +247,7 @@ (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m r2) -> (Source m (SourceFunctor (SinkFunctor d1 x) b) b -> Coroutine (SourceFunctor (SinkFunctor d1 x) b) m r3) ->- Coroutine d m ([x], r1, r2, r3)+ Coroutine d m ((), r1, r2, r3) splitToConsumers s source trueConsumer falseConsumer edgeConsumer = pipe (\true-> pipe@@ -279,21 +260,17 @@ -- | Given a 'Splitter', a 'Source', and two consumer functions, 'splitInputToConsumers' runs the splitter on the source -- and feeds the splitter's /true/ and /false/ outputs, respectively, to the two consumers.-splitInputToConsumers :: forall m a d d1 x b. (ParallelizableMonad m, d1 ~ SinkFunctor d x, AncestorFunctor a d) =>+splitInputToConsumers :: forall m a d d1 x b. (MonadParallel m, d1 ~ SinkFunctor d x, AncestorFunctor a d) => Bool -> Splitter m x b -> Source m a x ->- (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m [x]) ->- (Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m [x]) ->- Coroutine d m [x]+ (Source m (SourceFunctor d1 x) x -> Coroutine (SourceFunctor d1 x) m ()) ->+ (Source m (SourceFunctor d x) x -> Coroutine (SourceFunctor d x) m ()) ->+ Coroutine d m () splitInputToConsumers parallel s source trueConsumer falseConsumer = pipePS parallel (\false-> pipePS parallel- (\true-> pipePS parallel- (split s source' true false)- consumeAndSuppress)+ (\true-> split s source' true false (nullSink :: Sink m d b)) trueConsumer) falseConsumer- >>= \(((extra, _), xs1), xs2)-> return (prependCommonPrefix xs1 xs2 extra)- where prependCommonPrefix (x:xs) (y:ys) tail = x : prependCommonPrefix xs ys tail- prependCommonPrefix _ _ tail = tail- source' :: Source m d x+ >> return ()+ where source' :: Source m d x source' = liftSource source
Control/Concurrent/SCC/XML.hs view
@@ -1,5 +1,5 @@ {- - Copyright 2009 Mario Blazevic+ Copyright 2009-2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. @@ -32,22 +32,26 @@ ) where +import Prelude hiding (mapM) import Control.Exception (assert)-import Control.Monad (liftM, when)+import Control.Monad (join, liftM, when) import Data.Char import qualified Data.Map as Map import Data.Maybe (fromJust, isJust, mapMaybe) import Data.List (find, stripPrefix) import qualified Data.Sequence as Seq import Data.Sequence ((|>))+import Data.Traversable (Traversable, mapM) import Numeric (readDec, readHex) import Debug.Trace (trace) -import Control.Concurrent.Coroutine+import Control.Monad.Coroutine+import Control.Monad.Parallel (MonadParallel(..))+ import Control.Concurrent.SCC.Streams import Control.Concurrent.SCC.Types-import Control.Concurrent.SCC.Combinators (groupMarks, splitterToMarker, parseNestedRegions)-import Control.Concurrent.SCC.Primitives (unparse)+import Control.Concurrent.SCC.Combinators (groupMarks, splitterToMarker, parseNestedRegions,+ findsTrueIn, findsFalseIn, teeConsumers) data Token = StartTag | EndTag | EmptyTag@@ -91,148 +95,164 @@ tokens :: Monad m => Splitter m Char (Boundary Token) tokens = Splitter $ \source true false edge->- let getContent = get source- >>= maybe (return []) content- content '<' = get source- >>= maybe (return "<") (\x-> tag x >> get source >>= maybe (return []) content)- content '&' = entity >> next content+ let getContent = getWith content source+ content '<' = getWith (\x-> tag x >> getWith content source) source+ content '&' = entity >> getWith content source content x = put false x- >>= cond getContent (return [x])- tag '?' = put edge (Start ProcessingInstruction)- >> putList "<?" true- >>= whenNull (put edge (Start ProcessingInstructionText)- >> processingInstruction)+ >> getContent+ tag '?' = do put edge (Start ProcessingInstruction)+ putList "<?" true+ put edge (Start ProcessingInstructionText)+ processingInstruction tag '!' = dispatchOnString source- (\other-> put edge (Point (ErrorToken ("Expecting <![CDATA[ or <!--, received "- ++ show ("<![" ++ other))))- >> return ("<!" ++ other))+ (\other-> put edge (Point (errorBadDeclarationType other))) [("--",- \match-> put edge (Start Comment)- >> putList match true- >>= whenNull (put edge (Start CommentText)- >> comment)),+ \match-> do put edge (Start Comment)+ putList match true+ put edge (Start CommentText)+ comment), ("[CDATA[",- \match-> put edge (Start StartMarkedSectionCDATA)- >> putList match true- >>= whenNull (put edge (End StartMarkedSectionCDATA)- >> markedSection))]+ \match-> do put edge (Start StartMarkedSectionCDATA)+ putList match true+ put edge (End StartMarkedSectionCDATA)+ markedSection)] tag '/' = {-# SCC "EndTag" #-} do put edge (Start EndTag) put true '<' put true '/'- x <- next (name ElementName)- put true x- when (x /= '>')- (put edge (Point (ErrorToken ("Invalid character " ++ show x ++ " in end tag")))- >> return ())+ next errorInputEndInEndTag+ (\x-> name ElementName x+ >>= maybe+ (put edge (Point errorInputEndInEndTag))+ (\x-> do put true x+ when (x /= '>') (put edge (Point (errorBadEndTag x))))) put edge (End EndTag)- return []- tag x | isNameStart x- = {-# SCC "StartTag" #-}- do put edge (Start StartTag)- put true '<'- y <- name ElementName x- z <- attributes y- w <- if z == '/'- then put true z >> put edge (Point EmptyTag)- >> get source+ tag x | isNameStart x = {-# SCC "StartTag" #-}+ put edge (Start StartTag)+ >> put true '<'+ >> name ElementName x >>= maybe- (put edge (Point (ErrorToken ("Missing '>' at the end of start tag.")))- >> return '>')- return- else return z- put true w- when (w /= '>') (put edge (Point (ErrorToken ("Invalid character " ++ show w- ++ " in start tag")))- >> return ())- put edge (End StartTag)- return []- tag x = put edge (Point (ErrorToken "Unescaped character '<' in content"))+ (put edge (Point errorInputEndInStartTag))+ (\y-> attributes y+ >>= maybe+ (put edge (Point errorInputEndInStartTag))+ startTagEnd)+ >> put edge (End StartTag)+ tag x = put edge (Point errorUnescapedContentLT) >> put false '<' >> put false x- >> return []- attributes x | isSpace x = put true x >> next attributes+ startTagEnd '/' = put true '/'+ >> put edge (Point EmptyTag)+ >> next errorInputEndInStartTag+ (\x-> put true x >> when (x /= '>') (put edge (Point (errorBadStartTag x))))+ startTagEnd '>' = put true '>'+ startTagEnd x = put true x+ >> put edge (Point (errorBadStartTag x))+ attributes x | isSpace x = put true x >> get source >>= mapJoinM attributes attributes x | isNameStart x- = do y <- name AttributeName x- when (y /= '=') (put edge (Point (ErrorToken ("Invalid character " ++ show y- ++ " following attribute name")))- >> return ())- q <- if y == '"' || y == '\''- then return y- else put true y >> get source- >>= maybe (put edge (Point (ErrorToken ("Truncated input after attribute name")))- >> return '"')- return- when- (q /= '"' && q /= '\'')- (put edge (Point (ErrorToken ("Invalid quote character " ++ show q)))- >> return ())- put true q- put edge (Start AttributeValue)- next (attributeValue q)- next attributes- attributes x = return x+ = name AttributeName x+ >>= mapJoinM+ (\y-> do when (y /= '=') (put edge (Point (errorBadAttribute y)))+ q <- if y == '"' || y == '\''+ then return y+ else put true y >> get source+ >>= maybe+ (put edge (Point errorInputEndInAttributeValue)+ >> return '"')+ return+ when (q /= '"' && q /= '\'') (put edge (Point (errorBadQuoteCharacter q)))+ put true q+ put edge (Start AttributeValue)+ get source+ >>= maybe+ (put edge (Point errorInputEndInAttributeValue)+ >> put edge (End AttributeValue))+ (attributeValue q)+ get source >>= mapJoinM attributes)+ attributes x = return (Just x) attributeValue q x | q == x = do put edge (End AttributeValue) put true x- attributeValue q '<' = do put edge (Start (ErrorToken "Invalid character '<' in attribute value."))+ attributeValue q '<' = do put edge (Start errorUnescapedAttributeLT) put true '<'- put edge (End (ErrorToken "Invalid character '<' in attribute value."))- next (attributeValue q)- attributeValue q '&' = entity >> next (attributeValue q)- attributeValue q x = put true x >> next (attributeValue q)+ put edge (End errorUnescapedAttributeLT)+ next errorInputEndInAttributeValue (attributeValue q)+ attributeValue q '&' = entity >> next errorInputEndInAttributeValue (attributeValue q)+ attributeValue q x = put true x >> next errorInputEndInAttributeValue (attributeValue q) processingInstruction = {-# SCC "PI" #-} dispatchOnString source (\other-> if null other- then (put edge (Point (ErrorToken "Unterminated processing instruction"))- >> return [])- else putList other true >>= whenNull processingInstruction)+ then put edge (Point errorInputEndInProcessingInstruction)+ else putList other true >> processingInstruction) [("?>",- \match-> put edge (End ProcessingInstructionText)- >> putList match true- >>= whenNull (put edge (End ProcessingInstruction)- >> getContent))]+ \match-> do put edge (End ProcessingInstructionText)+ putList match true+ put edge (End ProcessingInstruction)+ getContent)] comment = {-# SCC "comment" #-} dispatchOnString source (\other-> if null other- then (put edge (Point (ErrorToken "Unterminated comment"))- >> return [])- else putList other true >>= whenNull comment)+ then put edge (Point errorInputEndInComment)+ else putList other true >> comment) [("-->",- \match-> put edge (End CommentText)- >> putList match true- >>= whenNull (put edge (End Comment)- >> getContent))]+ \match-> do put edge (End CommentText)+ putList match true+ put edge (End Comment)+ getContent)] markedSection = {-# SCC "<![CDATA[" #-} dispatchOnString source (\other-> if null other- then (put edge (Point (ErrorToken "Unterminated marked section"))- >> return [])- else putList other true >>= whenNull markedSection)+ then put edge (Point errorInputEndInMarkedSection)+ else putList other true >> markedSection) [("]]>",- \match-> put edge (Start EndMarkedSection)- >> putList match true- >>= whenNull (put edge (End EndMarkedSection)- >> getContent))]- entity = do put edge (Start EntityReferenceToken)- put true '&'- x <- next (name EntityName)- when (x /= ';') (put edge (Point (ErrorToken ("Invalid character " ++ show x- ++ " ends entity name.")))- >> return ())- put true x- put edge (End EntityReferenceToken)- name token x | isNameStart x = {-# SCC "name" #-} - do put edge (Start token)- put true x- next (nameTail token)- name _ x = do put edge (Point (ErrorToken ("Invalid character " ++ show x ++ " in attribute value.")))- return x+ \match-> do put edge (Start EndMarkedSection)+ putList match true+ put edge (End EndMarkedSection)+ getContent)]+ entity = put edge (Start EntityReferenceToken)+ >> put true '&'+ >> next errorInputEndInEntityReference+ (\x-> name EntityName x+ >>= maybe + (put edge (Point errorInputEndInEntityReference))+ (\x-> do when (x /= ';') (put edge (Point (errorBadEntityReference x)))+ put true x))+ >> put edge (End EntityReferenceToken)+ name token x | isNameStart x = {-# SCC "name" #-}+ put edge (Start token)+ >> put true x+ >> get source+ >>= maybe+ (put edge (End token) >> return Nothing)+ (nameTail token)+ name _ x = return (Just x) nameTail token x = if isNameChar x || x == ':'- then put true x >> next (nameTail token)- else put edge (End token) >> return x- next f = {-# SCC "next" #-} get' source >>= f+ then put true x+ >> get source+ >>= maybe+ (put edge (End token) >> return Nothing)+ (nameTail token)+ else put edge (End token) >> return (Just x)+ next error f = get source+ >>= maybe (put edge (Point error)) f in getContent +errorInputEndInComment = ErrorToken "Unterminated comment"+errorInputEndInMarkedSection = ErrorToken "Unterminated marked section"+errorInputEndInStartTag = ErrorToken "Missing '>' at the end of start tag."+errorInputEndInEndTag = ErrorToken "End of input in end tag"+errorInputEndInAttributeValue = ErrorToken "Truncated input after attribute name"+errorInputEndInEntityReference = ErrorToken "End of input in entity reference"+errorInputEndInProcessingInstruction = ErrorToken "Unterminated processing instruction"+errorBadQuoteCharacter q = ErrorToken ("Invalid quote character " ++ show q)+errorBadStartTag x = ErrorToken ("Invalid character " ++ show x ++ " in start tag")+errorBadEndTag x = ErrorToken ("Invalid character " ++ show x ++ " in end tag")+errorBadAttribute x = ErrorToken ("Invalid character " ++ show x ++ " following attribute name")+errorBadAttributeValue x = ErrorToken ("Invalid character " ++ show x ++ " in attribute value.")+errorBadEntityReference x = ErrorToken ("Invalid character " ++ show x ++ " ends entity name.")+errorBadDeclarationType other = ErrorToken ("Expecting <![CDATA[ or <!--, received " ++ show ("<![" ++ other))+errorUnescapedContentLT = ErrorToken "Unescaped character '<' in content"+errorUnescapedAttributeLT = ErrorToken "Invalid character '<' in attribute value."+ -- | The XML token parser. This parser converts plain text to parsed text, which is a precondition for using the -- remaining XML components. parseTokens :: Monad m => Parser m Char Token@@ -282,16 +302,16 @@ pourRestOfRegion :: forall m a1 a2 a3 d. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d) => Token -> Source m a1 (Markup Token Char) -> Sink m a2 (Markup Token Char) -> Sink m a3 (Markup Token Char)- -> Coroutine d m (Maybe [Markup Token Char])+ -> Coroutine d m Bool pourRestOfRegion token source sink endSink = get source >>= maybe- (return $ Just [])+ (return False) (\x-> case x of Markup (End token') | token == token' -> put endSink x- >>= cond (return Nothing) (return $ Just [x])+ >> return True Content y -> put sink x- >>= cond (pourRestOfRegion token source sink endSink) (return $ Just [x])+ >> pourRestOfRegion token source sink endSink _ -> error ("Expected rest of " ++ show token ++ ", received " ++ show x)) pourRestOfTag :: forall m a1 a2 d. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) =>@@ -309,74 +329,62 @@ findEndTag :: forall m a1 a2 a3 d. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d, AncestorFunctor a3 d) => Source m a1 (Markup Token Char) -> Sink m a2 (Markup Token Char) -> Sink m a3 (Markup Token Char) -> String- -> Coroutine d m [Markup Token Char]+ -> Coroutine d m () findEndTag source sink endSink name = find where- find = get source- >>= maybe- (return [])- (\x-> case x- of Markup (Start EndTag) -> do (tokens, mn) <- getElementName source (x :)- maybe- (return tokens)- (\name'-> if name == name'- then putList tokens endSink- >>= whenNull- (pourRestOfTag source endSink- >> return [])- else putList tokens sink- >>= whenNull- (pourRestOfTag source sink- >> find))- mn- Markup (Start StartTag) -> do (tokens, mn) <- getElementName source (x :)- maybe- (return tokens)- (\name'-> putList tokens sink- >>= whenNull- (if name == name'- then pourRestOfTag source sink- >>= cond- (findEndTag source sink sink name)- (return [])- >>= whenNull find- else pourRestOfTag source sink- >> find))- mn- _ -> put sink x- >>= cond find (return [x]))+ find = getWith consumeOne source+ consumeOne x@(Markup (Start EndTag)) = do (tokens, mn) <- getElementName source (x :)+ maybe+ (return ())+ (\name'-> if name == name'+ then do putList tokens endSink+ pourRestOfTag source endSink+ return ()+ else do putList tokens sink+ pourRestOfTag source sink+ find)+ mn+ consumeOne x@(Markup (Start StartTag)) = do (tokens, mn) <- getElementName source (x :)+ maybe+ (return ())+ (\name'-> putList tokens sink+ >> if name == name'+ then pourRestOfTag source sink+ >>= flip when (findEndTag source sink sink name)+ >> find+ else pourRestOfTag source sink+ >> find)+ mn+ consumeOne x = put sink x >> find findStartTag :: forall m a1 a2 d. (Monad m, AncestorFunctor a1 d, AncestorFunctor a2 d) => Source m a1 (Markup Token Char) -> Sink m a2 (Markup Token Char)- -> Coroutine d m (Either [Markup Token Char] (Markup Token Char))+ -> Coroutine d m (Maybe (Markup Token Char)) findStartTag source sink = get source >>= maybe- (return $ Left [])- (\x-> case x of Markup (Start StartTag) -> return $ Right x+ (return Nothing)+ (\x-> case x of Markup (Start StartTag) -> return $ Just x _ -> put sink x- >>= cond (findStartTag source sink) (return $ Left [x]))+ >> findStartTag source sink) -- | Splits all top-level elements with all their content to /true/, all other input to /false/. element :: Monad m => Splitter m (Markup Token Char) () element = Splitter $ \source true false edge-> let split0 = findStartTag source false- >>= either return- (\x-> put edge ()- >> put true x- >>= cond- (do (tokens, mn) <- getElementName source id- maybe- (putList tokens true)- (\name-> putList tokens true- >>= whenNull- (pourRestOfTag source true- >>= cond- (split1 name)- split0))- mn)- (return [x]))+ >>= maybe (return ())+ (\x-> do put edge ()+ put true x+ (tokens, mn) <- getElementName source id+ maybe+ (putList tokens true)+ (\name-> putList tokens true+ >> pourRestOfTag source true+ >>= cond+ (split1 name)+ split0)+ mn) split1 name = findEndTag source true true name- >>= whenNull split0+ >> split0 in split0 -- | Splits the content of all top-level elements to /true/, their tags and intervening input to /false/.@@ -384,87 +392,68 @@ elementContent = Splitter $ \source true false edge-> let split0 = findStartTag source false- >>= either return- (\x-> put false x- >>= cond- (do (tokens, mn) <- getElementName source id- maybe- (putList tokens false)- (\name-> putList tokens false- >>= whenNull (pourRestOfTag source false- >>= cond- (put edge ()- >> split1 name)- split0))- mn)- (return [x]))+ >>= maybe (return ())+ (\x-> do put false x+ (tokens, mn) <- getElementName source id+ maybe+ (putList tokens false)+ (\name-> putList tokens false+ >> pourRestOfTag source false+ >>= cond+ (put edge ()+ >> split1 name)+ split0)+ mn) split1 name = findEndTag source true false name- >>= whenNull split0+ >> split0 in split0 -- | Similiar to @('Control.Concurrent.SCC.Combinators.having' 'element')@, except it runs the argument splitter -- only on each element's start tag, not on the entire element with its content.-elementHavingTag :: forall m b. ParallelizableMonad m =>+elementHavingTag :: forall m b. MonadParallel m => Splitter m (Markup Token Char) b -> Splitter m (Markup Token Char) b elementHavingTag test = isolateSplitter $ \ source true false edge -> let split0 = findStartTag source false- >>= either return+ >>= maybe (return ()) (\x-> do (tokens, mn) <- getElementName source (x :) maybe- (return tokens)+ (return ()) (\name-> do (hasContent, rest) <- pipe (pourRestOfTag source) getList let tag = tokens ++ rest- (_, (unconsumed, maybeTrue, (), maybeEdge))- <- pipe- (putList tag)- (\tag-> splitToConsumers- test- tag- get- consumeAndSuppress- get)- if isJust maybeTrue || isJust maybeEdge- then maybe (return True) (put edge) maybeEdge- >> putList tag true- >>= whenNull (split1 hasContent true name)- else putList tag false- >>= whenNull (split1 hasContent false name))+ ((), found) <- pipe (putList tag) (findsTrueIn test)+ case found of Just mb -> maybe (return ()) (put edge) mb+ >> putList tag true+ >> split1 hasContent true name+ Nothing -> putList tag false+ >> split1 hasContent false name) mn)- split1 hasContent sink name = if hasContent- then findEndTag source sink sink name >>= whenNull split0- else split0+ split1 hasContent sink name = when hasContent (findEndTag source sink sink name)+ >> split0 in split0 -- | Splits every attribute specification to /true/, everything else to /false/. attribute :: Monad m => Splitter m (Markup Token Char) () attribute = Splitter $ \source true false edge->- let split0 = get source- >>= maybe- (return [])- (\x-> case x of Markup (Start AttributeName)- -> put edge ()- >> put true x- >>= cond- (pourRestOfRegion AttributeName source true true- >>= maybe split1 return)- (return [x])- _ -> put false x- >>= cond split0 (return [x]))- split1 = get source- >>= maybe- (return [])- (\x-> case x of Markup (Start AttributeValue)- -> put true x- >>= cond- (pourRestOfRegion AttributeValue source true true- >>= maybe split0 return)- (return [x])- _ -> put true x- >>= cond split1 (return [x]))+ let split0 = getWith+ (\x-> case x+ of Markup (Start AttributeName) -> do put edge ()+ put true x+ pourRestOfRegion AttributeName source true true+ >>= flip when split1+ _ -> put false x >> split0)+ source+ split1 = getWith+ (\x-> case x+ of Markup (Start AttributeValue)+ -> put true x+ >> pourRestOfRegion AttributeValue source true true+ >>= flip when split0+ _ -> put true x >> split1)+ source in split0 -- | Splits every element name, including the names of nested elements and names in end tags, to /true/, all the rest of@@ -481,71 +470,52 @@ attributeValue = Splitter (splitSimpleRegions AttributeValue) splitSimpleRegions token source true false edge = split- where split = get source- >>= maybe- (return [])- (\x-> case x of Markup (Start token') | token == token'- -> put false x- >>= cond- (put edge ()- >> pourRestOfRegion token source true false- >>= maybe split return)- (return [x])- _ -> put false x- >>= cond split (return [x]))+ where split = getWith consumeOne source+ consumeOne x@(Markup (Start token')) | token == token' = put false x+ >> put edge ()+ >> pourRestOfRegion token source true false+ >>= flip when split+ consumeOne x = put false x >> split -- | Behaves like 'Control.Concurrent.SCC.Combinators.having', but the right-hand splitter works on plain instead of -- marked-up text. This allows regular 'Char' splitters to be applied to parsed XML.-havingText :: forall m b1 b2. ParallelizableMonad m =>+havingText :: forall m b1 b2. MonadParallel m => Bool -> Splitter m (Markup Token Char) b1 -> Splitter m Char b2 -> Splitter m (Markup Token Char) b1-havingText parallel chunker tester =- isolateSplitter $ \ source true false edge ->- let test Nothing chunk = pour chunk false >> return []- test (Just mb) chunk = pipe- (\sink1-> pipe (tee chunk sink1) getList)- (\chunk-> liftM snd $- pipe- (transduce unparse chunk)- (\chunk-> splitToConsumers tester chunk- (liftM isJust . get)- consumeAndSuppress- (liftM isJust . get)))- >>= \(((), prefix), (_, anyTrue, (), anyEdge))->- if anyTrue || anyEdge- then maybe (return True) (put edge) mb- >> putList prefix true- >>= whenNull (pour chunk true >> return [])- else putList prefix false- >>= whenNull (pour chunk false >> return [])- in liftM fst $- pipePS parallel- (transduce (splitterToMarker chunker) source)- (flip groupMarks test)+havingText parallel chunker tester = isolateSplitter havingText' where+ havingText' source true false edge =+ let test Nothing chunk = pour chunk false+ test (Just mb) chunk = teeConsumers False getList (findsTrueIn tester . mapMaybeSource justContent) chunk+ >>= \(chunk, found)->+ if isJust found+ then maybe (return ()) (put edge) mb+ >> putList chunk true+ else putList chunk false+ in liftM fst $+ pipePS parallel+ (transduce (splitterToMarker chunker) source)+ (flip groupMarks test) -- | Behaves like 'Control.Concurrent.SCC.Combinators.havingOnly', but the right-hand splitter works on plain instead of -- marked-up text. This allows regular 'Char' splitters to be applied to parsed XML.-havingOnlyText :: forall m b1 b2. ParallelizableMonad m =>+havingOnlyText :: forall m b1 b2. MonadParallel m => Bool -> Splitter m (Markup Token Char) b1 -> Splitter m Char b2 -> Splitter m (Markup Token Char) b1-havingOnlyText parallel chunker tester =- isolateSplitter $ \ source true false edge ->- let test Nothing chunk = pour chunk false >> return []- test (Just mb) chunk = pipe- (\sink1-> pipe (tee chunk sink1) getList)- (\chunk-> liftM snd $- pipe- (transduce unparse chunk)- (\chunk-> splitToConsumers tester chunk- consumeAndSuppress- (liftM isJust . get)- consumeAndSuppress))- >>= \(((), prefix), (_, (), anyFalse, ()))->- if anyFalse- then putList prefix false- >>= whenNull (pour chunk false >> return [])- else maybe (return True) (put edge) mb- >> putList prefix true- >>= whenNull (pour chunk true >> return [])- in liftM fst $- pipePS parallel- (transduce (splitterToMarker chunker) source)- (flip groupMarks test)+havingOnlyText parallel chunker tester = isolateSplitter havingOnlyText' where+ havingOnlyText' source true false edge =+ let test Nothing chunk = pour chunk false+ test (Just mb) chunk = teeConsumers False getList (findsFalseIn tester . mapMaybeSource justContent) chunk+ >>= \(chunk, found)->+ if found+ then putList chunk false+ else maybe (return ()) (put edge) mb+ >> putList chunk true+ in liftM fst $+ pipePS parallel+ (transduce (splitterToMarker chunker) source)+ (flip groupMarks test)++justContent (Content x) = Just x+justContent _ = Nothing++mapJoinM :: (Monad m, Monad t, Traversable t) => (a -> m (t b)) -> t a -> m (t b)+mapJoinM f ta = mapM f ta >>= return . join+
Makefile view
@@ -1,32 +1,42 @@-Executables=test test-prof shsh shsh-prof+Executables=test test-prof test-coroutine test-parallel shsh shsh-prof LibraryFiles=$(addprefix Control/Concurrent/SCC/, \ Streams.hs Types.hs Primitives.hs Combinators.hs Components.hs XML.hs) \- Control/Concurrent/Coroutine.hs Control/Concurrent/Configuration.hs+ Control/Monad/Parallel.hs Control/Monad/Coroutine.hs \+ Control/Monad/Coroutine/SuspensionFunctors.hs Control/Monad/Coroutine/Nested.hs \+ Control/Concurrent/Configuration.hs DocumentationFiles=$(LibraryFiles)-OptimizingOptions=-O2 -threaded -hidir obj -odir obj+OptimizingOptions=-O -threaded -hidir obj -odir obj ProfilingOptions=-prof -auto-all -hidir prof -odir prof all: $(Executables) doc/index.html docs: doc/index.html -test: $(LibraryFiles) Test.hs | obj- ghc --make Test.hs -o test $(OptimizingOptions)+test: Test.hs $(LibraryFiles) | obj+ ghc --make $< -o $@ $(OptimizingOptions) -test-prof: $(LibraryFiles) Test.hs | prof- ghc --make Test.hs -o test-prof $(ProfilingOptions)+test-prof: Test.hs $(LibraryFiles) | prof+ ghc --make $< -o $@ $(ProfilingOptions) -shsh: $(LibraryFiles) Shell.hs | obj- ghc --make Shell.hs -o shsh $(OptimizingOptions)+test-coroutine: TestCoroutine.hs Control/Monad/Coroutine.hs Control/Monad/Coroutine/*.hs | obj+ ghc --make $< -o $@ $(OptimizingOptions) -shsh-prof: $(LibraryFiles) Shell.hs | prof- ghc --make Shell.hs -o shsh-prof $(ProfilingOptions)+test-parallel: TestParallel.hs Control/Monad/Parallel.hs | obj+ ghc --make $< -o $@ $(OptimizingOptions) +shsh: Shell.hs $(LibraryFiles) | obj+ ghc --make $< -o $@ $(OptimizingOptions)++shsh-prof: Shell.hs $(LibraryFiles) | prof+ ghc --make $< -o $@ $(ProfilingOptions)+ doc/index.html: $(DocumentationFiles)- haddock -h -o doc $^+ haddock -v -h -o doc \+ -i http://www.haskell.org/ghc/docs/latest/html/libraries/base,/usr/share/doc/ghc/libraries/base/base.haddock \+ $^ obj prof: mkdir -p $@ clean:- rm -r obj/* prof/* doc/* $(Executables)+ rm -r obj/* prof/* doc/* dist/* $(Executables)
Shell.hs view
@@ -1,6 +1,6 @@ {- - Copyright 2008-2009 Mario Blazevic+ Copyright 2008-2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. @@ -19,7 +19,7 @@ module Main where -import Prelude hiding (appendFile, interact, last, sequence)+import Prelude hiding (appendFile, interact, id, last, sequence) import Data.List (intersperse, partition) import Data.Char (isAlphaNum) import Data.Maybe (fromJust)@@ -45,7 +45,7 @@ hGetChar, hGetContents, hPutChar, hFlush, hIsEOF, hClose, putChar, isEOF, stdout) import Control.Concurrent.Configuration (Component, atomic, showComponentTree, usingThreads, with)-import Control.Concurrent.Coroutine+import Control.Monad.Coroutine import Control.Concurrent.SCC.Streams import Control.Concurrent.SCC.Types import Control.Concurrent.SCC.Components hiding ((&&), (||))@@ -428,7 +428,7 @@ compile UnitTag (FileProducer path) = Compiled (ProducerTag CharTag) (fromFile path) compile UnitTag StdInProducer = Compiled (ProducerTag CharTag) fromStdIn compile inputTag (FromList string) = Compiled (ProducerTag CharTag) (atomic "putList" 1 $ Producer $- \sink-> putList string sink >> return ())+ \sink-> putList string sink) compile inputTag (FileConsumer path) = Compiled (ConsumerTag CharTag) (toFile path) compile inputTag (FileAppend path) = Compiled (ConsumerTag CharTag) (appendFile path) compile inputTag Suppress = Compiled (ConsumerTag inputTag) suppress@@ -446,7 +446,6 @@ lift (hSetBuffering stdin NoBuffering >> hSetBuffering stdout NoBuffering) interleave source stdin pid stdout sink- return [] interleave :: forall a1 a2 d. (AncestorFunctor a1 d, AncestorFunctor a2 d) => Source IO a1 Char -> Handle -> Process.ProcessHandle -> Handle -> Sink IO a2 Char -> Coroutine d IO ()@@ -458,19 +457,16 @@ >>= maybe (lift (hPutChar stdin x) >> interleave2) (const interleave2))- interleave2 = canPut sink- >>= flip when (lift (hReady stdout)- >>= flip when (lift (hGetChar stdout)- >>= put sink- >> return ())- >> interleave1)- interleaveEnd = canPut sink- >>= flip when (lift (hIsEOF stdout)- >>= cond- (lift $ hClose stdout)- (lift (hGetChar stdout)- >>= put sink- >> interleaveEnd))+ interleave2 = lift (hReady stdout)+ >>= flip when (lift (hGetChar stdout)+ >>= put sink)+ >> interleave1+ interleaveEnd = lift (hIsEOF stdout)+ >>= cond+ (lift $ hClose stdout)+ (lift (hGetChar stdout)+ >>= put sink+ >> interleaveEnd) compile inputTag (Select e) = case compile inputTag e of Compiled (SplitterTag tag _) s -> Compiled (TransducerTag tag tag) (select s) Compiled tag _ -> TypeError tag (SplitterTag inputTag AnyTag) e@@ -501,16 +497,15 @@ compile inputTag (Substitute replacement) = wrapGenericProducerIntoTransducer substitute inputTag replacement compile inputTag ExecuteTransducer = Compiled (TransducerTag CharTag CharTag) (atomic "execute" ioCost $ Transducer execute)- where execute :: forall a1 a2 d. OpenTransducer IO a1 a2 d Char Char+ where execute :: forall a1 a2 d. OpenTransducer IO a1 a2 d Char Char () execute source sink = do let (source' :: Source IO d Char) = liftSource source ((), command) <- pipe (pour source') getList (Nothing, Just stdout, Nothing, pid) <- lift (Process.createProcess (Process.shell command){Process.std_out= Process.CreatePipe}) produce (with $ fromHandle stdout True) sink- return [] -compile inputTag IdentityTransducer = Compiled (TransducerTag inputTag inputTag) asis+compile inputTag IdentityTransducer = Compiled (TransducerTag inputTag inputTag) id compile inputTag Count = Compiled (TransducerTag inputTag IntTag) count compile inputTag@(ListTag itemTag) Concatenate = Compiled (TransducerTag inputTag itemTag) concatenate compile inputTag Concatenate = TypeError inputTag (ListTag AnyTag) Concatenate@@ -703,7 +698,7 @@ -> tryComponentCast tag2 tag1 t2 right (\t2'-> Compiled tag1 (combinator t1 t2')) combineSplitterAndBranches :: forall x.- (forall x b cc. Branching cc IO x [x] => SplitterComponent IO x b -> Component cc -> Component cc -> Component cc)+ (forall x b cc. Branching cc IO x () => SplitterComponent IO x b -> Component cc -> Component cc -> Component cc) -> TypeTag x -> Expression -> Expression -> Expression -> Expression combineSplitterAndBranches combinator inputTag splitter true false = case (compile inputTag splitter, compile inputTag true, compile inputTag false)
Test.hs view
@@ -1,5 +1,5 @@ {- - Copyright 2008-2009 Mario Blazevic+ Copyright 2008-2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. @@ -19,7 +19,7 @@ module Main where import Control.Concurrent.Configuration-import Control.Concurrent.Coroutine+import Control.Monad.Coroutine import Control.Concurrent.SCC.Streams import Control.Concurrent.SCC.Types import qualified Control.Concurrent.SCC.Combinators as Combinator@@ -39,7 +39,7 @@ import qualified Data.Sequence as Seq import Data.Sequence (Seq, (|>), (><), ViewL (EmptyL, (:<))) import Debug.Trace (trace)-import Prelude hiding (even, last)+import Prelude hiding (even, id, last) import qualified Prelude import Test.QuickCheck (Arbitrary, Gen, Property, -- CoArbitrary, Positive(Positive), arbitrary, coarbitrary, label, classify, choose, oneof, sized, quickCheck, variant, (==>))@@ -60,9 +60,9 @@ main = mapM_ quickCheck tests -tests = [label "pipe" $ \(input :: [Int])-> runCoroutine (pipe (putList input) getList) == Just ([], input),+tests = [label "pipe" $ \(input :: [Int])-> runCoroutine (pipe (putList input) getList) == Just ((), input), label "pour" prop_pour,- label "asis" prop_asis,+ label "id" prop_id, label "suppress" prop_suppress, label "substitute" prop_substitute, label "prepend" prop_prepend,@@ -77,27 +77,27 @@ (putList s) (consume $ with $ uppercase >-> atomic "getList" 1 (Consumer getList)))- == Just ([], map toUpper s),+ == Just ((), map toUpper s), label "uppercase <<-" $ \s-> runCoroutine (pipe (produce $ with $ atomic "putList" 1 (Producer (putList s)) >-> uppercase) getList)- == Just ([], map toUpper s),- label "uppercase `join` asis" $ \s-> transducerOutput (uppercase `join` asis) s == map toUpper s ++ s,+ == Just ((), map toUpper s),+ label "uppercase `join` id" $ \s-> transducerOutput (uppercase `join` id) s == map toUpper s ++ s, label "prepend >-> append" (\(s :: String) prefix suffix-> transducerOutput (prepend (fromList prefix) >-> append (fromList suffix)) s == prefix ++ s ++ suffix),- label "prepend == (`join` asis) . substitute" $+ label "prepend == (`join` id) . substitute" $ \(s :: String) prefix-> transducerOutput (prepend (fromList prefix)) s- == transducerOutput (substitute (fromList prefix) `join` asis) s,- label "append == (asis `join`) . substitute" $+ == transducerOutput (substitute (fromList prefix) `join` id) s,+ label "append == (id `join`) . substitute" $ \(s :: String) suffix-> transducerOutput (append (fromList suffix)) s- == transducerOutput (asis `join` substitute (fromList suffix)) s,+ == transducerOutput (id `join` substitute (fromList suffix)) s, label "whitespace" $ \s-> splitterOutputs whitespace s == (filter isSpace s, filter (not . isSpace) s),- label "ifs everything asis asis" $ \(s :: [TestEnum])-> transducerOutput (ifs everything asis asis) s == s,+ label "ifs everything id id" $ \(s :: [TestEnum])-> transducerOutput (ifs everything id id) s == s, label "substring" $ \s (c :: TestEnum)-> splitterOutputs (substring [c]) s == (filter (==c) s, filter (/=c) s),- label "ifs (substring X) uppercase asis" $- \s (LowercaseLetter c)-> transducerOutput (ifs (substring [c]) uppercase asis) s+ label "ifs (substring X) uppercase id" $+ \s (LowercaseLetter c)-> transducerOutput (ifs (substring [c]) uppercase id) s == map (\x-> if x == c then toUpper x else x) s, label "parseSubstring" $ \s (c :: TestEnum)-> transducerOutput (parseSubstring [c] >-> select markedContent >-> unparse)@@ -113,11 +113,11 @@ label "parseRegions substring == parseSubstring" prop_substringVsParse, label "count >-> toString >-> concatenate" $ \(s :: [TestEnum])-> transducerOutput (count >-> toString >-> concatenate) s == show (length s),- label "foreach whitespace asis (prepend \"[\" >-> append \"]\")" $- \s-> transducerOutput (foreach whitespace asis (prepend (fromList "[") >-> append (fromList "]"))) s+ label "foreach whitespace id (prepend \"[\" >-> append \"]\")" $+ \s-> transducerOutput (foreach whitespace id (prepend (fromList "[") >-> append (fromList "]"))) s == mapWords (("[" ++) . (++ "]")) s,- label "foreach whitespace asis (count >-> toString >-> concatenate)" $- \s-> transducerOutput (foreach whitespace asis (count >-> toString >-> concatenate)) s+ label "foreach whitespace id (count >-> toString >-> concatenate)" $+ \s-> transducerOutput (foreach whitespace id (count >-> toString >-> concatenate)) s == mapWords (show . length) s, label "uppercase `wherever` (snot whitespace `having` substring X)" $ \s1 s2-> not (null s1) && length s1 < length s2 ==> classify (not (s1 `isInfixOf` s2)) "trivial" $@@ -143,20 +143,20 @@ && transducerOutput (uppercase `wherever` (last letters)) "Hello, World" == "Hello, WORLD"), label "(select (prefix letters))" (transducerOutput (select (prefix letters)) "Hello, World!" == "Hello"),- label "(foreach letters (count >-> toString >-> concatenate) asis)"- (transducerOutput (foreach letters (count >-> toString >-> concatenate) asis) "Hola, Mundo!" == "4, 5!"),- label "(foreach (letters `having` prefix (substring \"H\")) uppercase asis)"+ label "(foreach letters (count >-> toString >-> concatenate) id)"+ (transducerOutput (foreach letters (count >-> toString >-> concatenate) id) "Hola, Mundo!" == "4, 5!"),+ label "(foreach (letters `having` prefix (substring \"H\")) uppercase id)" (transducerOutput (foreach (letters `having` prefix (substring "H")) uppercase- asis)+ id) "Hello, World! Hola, Mundo!" == "HELLO, World! HOLA, Mundo!"),- label "(foreach (letters `having` suffix (substring \"o\")) uppercase asis)"+ label "(foreach (letters `having` suffix (substring \"o\")) uppercase id)" (transducerOutput (foreach (letters `having` suffix (substring "o")) uppercase- asis)+ id) "Hello, World! Hola, Mundo!" == "HELLO, World! Hola, MUNDO!"), @@ -204,10 +204,10 @@ prop_pour :: [Int] -> Bool prop_pour input = runCoroutine (pipe (putList input) (\source-> pipe (\sink-> pour source sink) getList))- == Just ([], ((), input))+ == Just ((), ((), input)) -prop_asis :: [Int] -> Bool-prop_asis input = transducerOutput asis input == input+prop_id :: [Int] -> Bool+prop_id input = transducerOutput id input == input prop_suppress :: [Int] -> Bool prop_suppress input = null (transducerOutput (consumeBy suppress :: TransducerComponent Identity Int ()) input)@@ -494,7 +494,7 @@ (\source-> pipe (\sink-> transduce t source sink) getList))- of Identity ([], ([], output)) -> output+ of Identity ((), ((), output)) -> output splitterOutputs :: SplitterComponent Identity x b -> [x] -> ([x], [x]) splitterOutputs s input = case runCoroutine (pipe@@ -502,8 +502,8 @@ (\source-> splitToConsumers (with s) source getList getList- consumeAndSuppress))- of Identity ([], ([], true, false, ())) -> (true, false)+ (mapMStream_ (const $ return ()))))+ of Identity ((), ((), true, false, ())) -> (true, false) splitterUnifiedOutput :: forall x b. SplitterComponent Identity x b -> [x] -> [Either (x, Bool) b] splitterUnifiedOutput s input =@@ -515,12 +515,12 @@ getList) where mapSplit :: forall a d. AncestorFunctor a d => SplitterComponent Identity x b -> Sink Identity a (Either (x, Bool) b) -> Source Identity d x- -> Coroutine d Identity ([x], (), (), ())+ -> Coroutine d Identity () mapSplit s sink source = let sink' = liftSink sink :: Sink Identity d (Either (x, Bool) b)- in splitToConsumers (with s) source- (flip (pourMap (Left . (\x-> (x, True)))) sink')- (flip (pourMap (Left . (\x-> (x, False)))) sink')- (flip (pourMap Right) sink')+ in split (with s) source+ (mapSink (Left . (\x-> (x, True))) sink')+ (mapSink (Left . (\x-> (x, False))) sink')+ (mapSink Right sink') splitterOutputChunks :: SplitterComponent Identity x b -> [x] -> [([x], Bool)] splitterOutputChunks s input = transducerOutput (foreach s@@ -542,17 +542,16 @@ where succeed x = let q' = q |> x in case head of Nothing -> follow previous tail q'- Just Nothing -> when (not previous) (put edge () >> return ())+ Just Nothing -> when (not previous) (put edge ()) >> follow False tail q'- Just (Just True) -> when (not previous) (put edge () >> return ())+ Just (Just True) -> when (not previous) (put edge ()) >> putList (Foldable.toList (Seq.viewl q')) true- >>= whenNull (follow True tail Seq.empty)+ >> follow True tail Seq.empty Just (Just False) -> putList (Foldable.toList (Seq.viewl q')) false- >>= whenNull (follow False tail Seq.empty)+ >> follow False tail Seq.empty fail = if find (maybe False isJust) trace2 == Just (Just (Just True))- then do when (not previous) (put edge () >> return ())- result <- putList (Foldable.toList (Seq.viewl q)) true- return result+ then do when (not previous) (put edge ())+ putList (Foldable.toList (Seq.viewl q)) true else putList (Foldable.toList (Seq.viewl q)) false in follow False (cycle (fst trace1 ++ [Just (Just $ snd trace1)])) Seq.empty
scc.cabal view
@@ -1,15 +1,15 @@ Name: scc-Version: 0.4+Version: 0.5 Cabal-Version: >= 1.2 Build-Type: Simple Synopsis: Streaming component combinators Category: Control, Combinators, Concurrency Tested-with: GHC Description:- SCC is a layered library of Streaming Component Combinators. The lowest layer defines the Coroutine monad transformer.- The next few layers add stream abstractions and nested producer-consumer coroutine pairs. On top of that are streaming- component types, a number of primitive streaming components and a set of component combinators. Finally, there is an- executable that exposes all framework functionality in a command-line shell.+ SCC is a layered library of Streaming Component Combinators. The lowest layer defines stream abstractions and nested+ producer-consumer coroutine pairs based on the Coroutine monad transformer. On top of that are streaming component+ types, a number of primitive streaming components and a set of component combinators. Finally, there is an executable+ that exposes all the framework functionality in a command-line shell. . The original library design is based on paper <http://conferences.idealliance.org/extreme/html/2006/Blazevic01/EML2006Blazevic01.html> .@@ -28,18 +28,18 @@ Executable shsh Main-is: Shell.hs- Other-Modules: Control.Concurrent.Coroutine,- Control.Concurrent.SCC.Streams, Control.Concurrent.SCC.Types,+ Other-Modules: Control.Concurrent.SCC.Streams, Control.Concurrent.SCC.Types, Control.Concurrent.SCC.Combinators, Control.Concurrent.SCC.Primitives, Control.Concurrent.SCC.XML, Control.Concurrent.Configuration, Control.Concurrent.SCC.Components- Build-Depends: base < 5, containers, transformers, parallel, process, readline, parsec >= 3.0 && < 4.0+ Build-Depends: base < 5, containers, transformers, monad-parallel, monad-coroutine,+ process, readline, parsec >= 3.0 && < 4.0 GHC-options: -threaded Library- Exposed-Modules: Control.Concurrent.Coroutine, Control.Concurrent.SCC.Streams, Control.Concurrent.SCC.Types,+ Exposed-Modules: Control.Concurrent.SCC.Streams, Control.Concurrent.SCC.Types, Control.Concurrent.SCC.Combinators, Control.Concurrent.SCC.Primitives, Control.Concurrent.SCC.XML, Control.Concurrent.Configuration, Control.Concurrent.SCC.Components- Build-Depends: base < 5, containers, transformers, parallel+ Build-Depends: base < 5, containers, transformers, monad-parallel, monad-coroutine GHC-prof-options: -auto-all