{-# LANGUAGE Trustworthy #-} -- Safe if eliminate GeneralizedNewtypeInstance {-# LANGUAGE Arrows #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} module Control.Arrow.Machine.Types ( -- * Stream transducer type ProcessT(), ProcessA, -- * Event type and utility Occasional' (..), Occasional (..), Event (), noEvent, end, ZeroEvent(..), condEvent, filterEvent, filterJust, filterLeft, filterRight, splitEvent, evMap, -- * Coroutine monad -- | Procedural coroutine monad that can await or yield values. -- -- Coroutines can be encoded to machines by `constructT` or so on and -- then put into `ProcessT` compositions. PlanT(..), Plan, MonadAwait (..), MonadYield (..), MonadStop (..), catchP, stopped, muted, -- * Constructing machines from plans constructT, repeatedlyT, construct, repeatedly, -- * Evolution monad -- | Time-evolution monad, or generalized plan monad. Evolution(..), packProc, awaitProc, yieldProc, -- * Running machines (at once) runT, runT_, run, run_, -- * Running machines (step-by-step) stepRun, stepYield, -- * Primitive machines - switches -- | Switches inspired by the Yampa library. -- Signature is almost same, but collection requirement is not only 'Functor', -- but 'Tv.Traversable'. This is because of side effects. switch, dSwitch, rSwitch, drSwitch, kSwitch, dkSwitch, gSwitch, dgSwitch, pSwitch, pSwitchB, dpSwitch, dpSwitchB, rpSwitch, rpSwitchB, drpSwitch, drpSwitchB, par, parB, -- * Primitive machines - other safe primitives fit, fitW, -- * Primitive machines - unsafe unsafeExhaust, ) where import qualified Control.Category as Cat import Data.Profunctor (Profunctor, dimap, rmap) import Data.Void import Control.Arrow import Control.Monad import Control.Monad.Trans import Control.Monad.State.Strict import Control.Monad.Reader import Control.Monad.Writer hiding ((<>)) import Control.Monad.Identity import Control.Monad.Trans.Cont import Control.Applicative import qualified Data.Foldable as Fd import Data.Traversable as Tv import Data.Semigroup (Semigroup ((<>))) import Data.Maybe (fromMaybe, isNothing, isJust) import qualified Control.Monad.Trans.Free.Church as F import GHC.Exts (build) -- | To get multiple outputs by one input, the `Phase` parameter is introduced. -- -- Once a value `Feed`ed, the machine is `Sweep`ed until it `Suspend`s. data Phase = Feed | Sweep | Suspend deriving (Eq, Show) instance Semigroup Phase where (<>) Feed _ = Feed (<>) _ Feed = Feed (<>) Suspend _ = Suspend (<>) _ Suspend = Suspend (<>) Sweep Sweep = Sweep instance Monoid Phase where mempty = Sweep mappend = (<>) type ProcType a b c = ProcessT a b c class Stepper m b c s | s -> m, s -> b, s -> c where feed :: s -> b -> m (c, s) sweep :: s -> b -> m (Maybe c, s) suspend :: s -> b -> c -- | The stream transducer arrow. -- -- To construct `ProcessT` instances, use `Control.Arrow.Machine.Plan.Plan`, -- `arr`, functions declared in `Control.Arrow.Machine.Utils`, -- or arrow combinations of them. -- -- See an introduction at "Control.Arrow.Machine" documentation. data ProcessT m b c = ProcessT { paFeed :: b -> m (c, ProcessT m b c), paSweep :: b -> m (Maybe c, ProcessT m b c), paSuspend :: !(b -> c) } -- | Isomorphic to ProcessT when 'a' is ArrowApply. type ProcessA a = ProcessT (ArrowMonad a) instance Stepper a b c (ProcessT a b c) where feed = paFeed sweep = paSweep suspend = paSuspend toProcessT :: (Monad m, Stepper m b c s) => s -> ProcessT m b c toProcessT s = ProcessT { paFeed = liftM (second toProcessT) . feed s, paSweep = liftM (second toProcessT) . sweep s, paSuspend = suspend s } {-# INLINE[2] toProcessT #-} -- For internal use class (Applicative f, Monad f) => ProcessHelper f where step :: (Monad m, Stepper m b c s) => s -> b -> m (f c, s) helperToMaybe :: f a -> Maybe a weakly :: a -> f a compositeStep :: (Monad m, Stepper m b p s1, Stepper m p c s2) => s1 -> s2 -> b -> m (f c, s1, s2) instance ProcessHelper Identity where step pa = liftM (first Identity) . feed pa helperToMaybe = Just . runIdentity weakly = Identity compositeStep sf test x = do (y, sf') <- feed sf x (z, test') <- feed test y return (return z, sf', test') instance ProcessHelper Maybe where step = sweep helperToMaybe = id weakly _ = Nothing compositeStep sf0 test0 x = do let y = suspend sf0 x (mt, test') <- sweep test0 y case mt of Just t -> return (Just t, sf0, test') Nothing -> next sf0 test' where next sf test = do (my, sf') <- sweep sf x case my of Just y -> next2 y sf' test Nothing -> return (Nothing, sf', test) next2 y sf test = do (t, test') <- feed test y return (Just t, sf, test') makePA :: Monad m => (forall f. ProcessHelper f => b -> m (f c, ProcessT m b c)) -> (b -> c) -> ProcessT m b c makePA h !sus = ProcessT { paFeed = liftM (first runIdentity) . h, paSweep = h, paSuspend = sus } data CompositeStep m b c s1 s2 where CompositeStep :: (Stepper m b p s1, Stepper m p c s2) => s1 -> s2 -> CompositeStep m b c s1 s2 instance Monad m => Stepper m b c (CompositeStep m b c s1 s2) where feed (CompositeStep s1 s2) x = do (fz, s1', s2') <- compositeStep s1 s2 x return (runIdentity fz, CompositeStep s1' s2') sweep (CompositeStep s1 s2) x = do (fz, s1', s2') <- compositeStep s1 s2 x return (fz, CompositeStep s1' s2') suspend (CompositeStep s1 s2) = suspend s2 . suspend s1 data IDStep m b c where IDStep :: IDStep (m :: * -> *) b b instance Monad m => Stepper m b c (IDStep m b c) where feed IDStep x = return (x, IDStep) sweep IDStep _ = return (Nothing, IDStep) suspend IDStep = id newtype ArrStep (m :: * -> *) b c = ArrStep (b -> c) instance Monad m => Stepper m b c (ArrStep m b c) where feed (ArrStep f) x = return (f x, ArrStep f) sweep (ArrStep f) _ = return (Nothing, ArrStep f) suspend (ArrStep f) = f data ParStep m b c s1 s2 where ParStep :: (Stepper m b1 c1 s1, Stepper m b2 c2 s2) => s1 -> s2 -> ParStep m (b1, b2) (c1, c2) s1 s2 instance Monad m => Stepper m b c (ParStep m b c s1 s2) where feed (ParStep f g) (x1, x2) = do (y1, f') <- feed f x1 (y2, g') <- feed g x2 return ((y1, y2), ParStep f' g') sweep (ParStep f g) (x1, x2) = do (my1, f') <- sweep f x1 (my2, g') <- sweep g x2 let y1 = fromMaybe (suspend f' x1) my1 -- suspend f ? y2 = fromMaybe (suspend g' x2) my2 r = if (isNothing my1 && isNothing my2) then Nothing else Just (y1, y2) return (r, ParStep f' g') suspend (ParStep f g) = suspend f *** suspend g -- |Natural transformation fit :: (Monad m, Monad m') => (forall p. m p -> m' p) -> ProcessT m b c -> ProcessT m' b c fit f pa = arr Identity >>> fitW runIdentity (\ar (Identity x) -> f (ar x)) pa -- |Experimental: more general fit. -- -- Should w be a comonad? fitW :: (Monad m, Monad m', Functor w) => (forall p. w p -> p) -> (forall p q. (p -> m q) -> w p -> m' q) -> ProcessT m b c -> ProcessT m' (w b) c fitW extr f pa = makePA (liftM (second $ fitW extr f) . f (step pa)) (extr >>> suspend pa) instance Monad m => Profunctor (ProcessT m) where dimap = dimapProc {-# INLINE dimap #-} dimapProc :: Monad m => (b->c)->(d->e)-> ProcType m c d -> ProcType m b e dimapProc f g pa = makePA (liftM (fmap g *** dimapProc f g) . step pa . f) (dimap f g (suspend pa)) {-# NOINLINE dimapProc #-} instance Monad m => Functor (ProcessT m i) where fmap = rmap instance Monad m => Applicative (ProcessT m i) where pure = arr . const pf <*> px = (pf &&& px) >>> arr (uncurry ($)) instance (Monad m, Semigroup o) => Semigroup (ProcessT m i o) where (<>) = liftA2 (<>) instance (Monad m, Monoid o) => Monoid (ProcessT m i o) where mempty = pure mempty mappend = liftA2 mappend instance Monad m => Cat.Category (ProcessT m) where id = idProc {-# INLINE id #-} g . f = compositeProc f g {-# INLINE (.) #-} instance Monad m => Arrow (ProcessT m) where arr = arrProc {-# INLINE arr #-} first pa = parProc pa idProc {-# INLINE first #-} second pa = parProc idProc pa {-# INLINE second #-} (***) = parProc {-# INLINE (***) #-} parProc :: Monad m => ProcType m b c -> ProcType m d e -> ProcType m (b, d) (c, e) parProc f g = toProcessT $ ParStep f g {-# INLINE [0] parProc #-} idProc :: Monad m => ProcType m b b idProc = let pa = makePA (\x -> return (weakly x, pa)) id in pa {-# NOINLINE idProc #-} arrProc :: Monad m => (b->c) -> ProcType m b c arrProc f = let pa = makePA (\x -> return (weakly (f x), pa)) f in pa {-# NOINLINE arrProc #-} -- |Composition is proceeded by the backtracking strategy. compositeProc :: Monad m => ProcType m b d -> ProcType m d c -> ProcType m b c compositeProc f0 g0 = ProcessT { paFeed = \x -> do (y, f') <- feed f0 x (z, g') <- feed g0 y return (z, compositeProc f' g'), paSweep = \x -> do (mz, g') <- sweep g0 $ suspend f0 x case mz of Just z -> return (Just z, compositeProc f0 g') Nothing -> btrk f0 g' x, paSuspend = suspend f0 >>> suspend g0 } where btrk f g x = do (my, f') <- sweep f x (mz, g') <- case my of Just y -> do (z, g') <- feed g y return (Just z, g') Nothing -> return (Nothing, g) return (mz, compositeProc f' g') {-# NOINLINE compositeProc #-} -- rules {-# RULES "ProcessT: id/*" forall g. compositeProc idProc g = g "ProcessT: */id" forall f. compositeProc f idProc = f "ProcessT: concat/concat" forall f g h. compositeProc (compositeProc f g) h = compositeProc f (compositeProc g h) "ProcessT: dimap/dimap" forall f g h i j. dimapProc f j (dimapProc g i h) = dimapProc (g . f) (j . i) h "ProcessT: dimap/arr" forall f g h. dimapProc f h (arrProc g) = arrProc (h . g . f) "ProcessT: arr***/par" forall f1 f2 g1 g2 h. compositeProc (parProc f1 (arrProc f2)) (compositeProc (parProc g1 g2) h) = compositeProc (parProc (compositeProc f1 g1) (dimapProc f2 id g2)) h "ProcessT: arr***/par-2" forall f1 f2 g1 g2. compositeProc (parProc f1 (arrProc f2)) (parProc g1 g2) = parProc (compositeProc f1 g1) (dimapProc f2 id g2) "ProcessT: par/***arr" forall f1 f2 g1 g2 h. compositeProc (parProc f1 f2) (compositeProc (parProc (arrProc g1) g2) h) = compositeProc (parProc (dimapProc id g1 f1) (compositeProc f2 g2)) h "ProcessT: par/***arr-2" forall f1 f2 g1 g2. compositeProc (parProc f1 f2) (parProc (arrProc g1) g2) = parProc (dimapProc id g1 f1) (compositeProc f2 g2) "ProcessT: first/par" forall f1 g1 g2 h. compositeProc (parProc f1 idProc) (compositeProc (parProc g1 g2) h) = compositeProc (parProc (compositeProc f1 g1) g2) h "ProcessT: first/par-2" forall f1 g1 g2. compositeProc (parProc f1 idProc) (parProc g1 g2) = parProc (compositeProc f1 g1) g2 "ProcessT: par/second" forall f1 f2 g2 h. compositeProc (parProc f1 f2) (compositeProc (parProc idProc g2) h) = compositeProc (parProc f1 (compositeProc f2 g2)) h "ProcessT: par/second-2" forall f1 f2 g2. compositeProc (parProc f1 f2) (parProc idProc g2) = parProc f1 (compositeProc f2 g2) "ProcessT: arr/arr" forall f g h. compositeProc (arrProc f) (compositeProc (arrProc g) h) = compositeProc (arrProc (g . f)) h "ProcessT: arr/arr-2" forall f g. compositeProc (arrProc f) (arrProc g) = arrProc (g . f) "ProcessT: arr/*" [1] forall f g. compositeProc (arrProc f) g = dimapProc f id g "ProcessT: */arr" [1] forall f g. compositeProc f (arrProc g) = dimapProc id g f "ProcessT: arr***arr" [1] forall f g. parProc (arrProc f) (arrProc g) = arrProc (f *** g) #-} instance Monad m => ArrowChoice (ProcessT m) where left pa0 = makePA (\eth -> sweep' pa0 eth) (left $ suspend pa0) where sweep' pa (Left x) = do (my, pa') <- step pa x return (Left <$> my, left pa') sweep' pa (Right d) = return (weakly (Right d), left pa) instance Monad m => ArrowLoop (ProcessT m) where loop pa = makePA (\x -> do (hyd, pa') <- step pa (x, loopSusD x) return (fst <$> hyd, loop pa')) (loop $ suspend pa) where loopSusD = loop (suspend pa >>> \(_, d) -> (d, d)) -- | Discrete events on a time line. -- Created and consumed by various transducers. data Event a = Event a | NoEvent | End instance Functor Event where fmap _ NoEvent = NoEvent fmap _ End = End fmap f (Event x) = Event (f x) instance Semigroup a => Semigroup (Event a) where Event x <> Event y = Event (x <> y) Event x <> _ = Event x _ <> Event y = Event y NoEvent <> _ = NoEvent _ <> NoEvent = NoEvent _ <> _ = End instance Semigroup a => Monoid (Event a) where mempty = End mappend = (<>) -- | Signals that can be absent(`NoEvent`) or end. -- For composite structure, `collapse` can be defined as monoid sum of all member occasionals. class Occasional' a where collapse :: a -> Event () -- | Occasional signals with creation methods. class Occasional' a => Occasional a where burst :: Event Void -> a instance (Occasional' a, Occasional' b) => Occasional' (a, b) where collapse (x, y) = collapse x `mappend` collapse y instance (Occasional a, Occasional b) => Occasional (a, b) where burst = burst &&& burst instance Occasional' (Event a) where collapse = (() <$) instance Occasional (Event a) where burst = fmap absurd noEvent :: Occasional a => a noEvent = burst NoEvent end :: Occasional a => a end = burst End data ZeroEvent = ZeroEvent deriving (Eq, Show, Enum, Bounded) instance Semigroup ZeroEvent where _ <> _ = ZeroEvent instance Monoid ZeroEvent where mempty = ZeroEvent mappend _ _ = ZeroEvent instance Occasional' ZeroEvent where collapse _ = mempty condEvent :: Bool -> Event a -> Event a condEvent _ End = End condEvent True ev = ev condEvent False _ = NoEvent filterEvent :: Arrow ar => (a -> Bool) -> ar (Event a) (Event a) filterEvent cond = filterJust <<< evMap mcond where mcond x | cond x = Just x | otherwise = Nothing filterJust :: Arrow ar => ar (Event (Maybe a)) (Event a) filterJust = arr filterJust' where filterJust' (Event (Just x)) = Event x filterJust' (Event Nothing) = NoEvent filterJust' NoEvent = NoEvent filterJust' End = End -- |Split an event stream. -- -- >>> run (filterLeft) [Left 1, Right 2, Left 3, Right 4] -- [1,3] filterLeft :: Arrow ar => ar (Event (Either a b)) (Event a) filterLeft = filterJust <<< evMap (either Just (const Nothing)) -- |Split an event stream. -- -- >>> run filterRight [Left 1, Right 2, Left 3, Right 4] -- [2,4] filterRight :: Arrow ar => ar (Event (Either a b)) (Event b) filterRight = filterJust <<< evMap (either (const Nothing) Just) -- |Split an event stream. -- -- >>> run (splitEvent >>> arr fst) [Left 1, Right 2, Left 3, Right 4] -- [1,3] -- -- >>> run (splitEvent >>> arr snd) [Left 1, Right 2, Left 3, Right 4] -- [2,4] splitEvent :: Arrow ar => ar (Event (Either a b)) (Event a, Event b) splitEvent = filterLeft &&& filterRight -- | Alias of "arr . fmap" -- -- While "ProcessT a (Event b) (Event c)" means a transducer from b to c, -- function b->c can be lifted into a transducer by fhis function. -- -- But in most cases you needn't call this function in proc-do notations, -- because `arr`s are completed automatically while desugaring. -- -- For example, -- -- @ -- proc x -> returnA -\< f \<$\> x -- @ -- -- is equivalent to -- -- @ -- evMap f -- @ evMap :: Arrow a => (b->c) -> a (Event b) (Event c) evMap = arr . fmap muted :: (Monad m, Occasional' b, Occasional c) => ProcessT m b c muted = arr collapse >>> repeatedly await >>> arr burst -- | A monad type represents time evolution of ProcessT newtype Evolution i o m r = Evolution { runEvolution :: Cont (ProcessT m i o) r } deriving (Functor, Applicative, Monad) instance Occasional o => MonadTrans (Evolution i o) where {-# INLINE lift #-} lift ma = Evolution $ cont $ \fmpf -> packProc (fmpf <$> ma) instance (MonadIO m, Occasional o) => MonadIO (Evolution i o m) where {-# INLINE liftIO #-} liftIO ma = lift $ liftIO ma data PlanF i o a where AwaitPF :: (i->a) -> a -> PlanF i o a YieldPF :: o -> a -> PlanF i o a StopPF :: PlanF i o a instance Functor (PlanF i o) where fmap g (AwaitPF f ff) = AwaitPF (g . f) (g ff) fmap g (YieldPF x r) = YieldPF x (g r) fmap _ StopPF = StopPF newtype PlanT i o m a = PlanT { freePlanT :: F.FT (PlanF i o) m a } deriving (Functor, Applicative, Monad) type Plan i o a = forall m. Monad m => PlanT i o m a packProc :: (Monad m, Occasional o) => m (ProcessT m i o) -> ProcessT m i o packProc !mp = ProcessT { paFeed = \ex -> mp >>= \p -> feed p ex , paSweep = \ex -> mp >>= \p -> sweep p ex, paSuspend = const noEvent } {-# INLINE[0] packProc #-} {-# RULES "ProcessT: return/packProc" forall p. return (packProc p) = p #-} {- "ProcessT: packProc/return" forall p. packProc (return p) = p -} instance MonadTrans (PlanT i o) where lift mx = PlanT $ lift mx {-# INLINE lift #-} instance MonadReader r m => MonadReader r (PlanT i o m) where ask = lift ask local f mx = PlanT $ local f (freePlanT mx) instance MonadWriter w m => MonadWriter w (PlanT i o m) where tell = lift . tell listen mx = PlanT $ listen (freePlanT mx) pass mx = PlanT $ pass (freePlanT mx) instance MonadState s m => MonadState s (PlanT i o m) where get = lift get put x = lift $ put x instance Monad m => Alternative (PlanT i o m) where empty = stop (<|>) = catchP instance Monad m => MonadPlus (PlanT i o m) where mzero = stop mplus = catchP instance MonadIO m => MonadIO (PlanT i o m) where liftIO = lift . liftIO {-# INLINE liftIO #-} class MonadAwait m a | m -> a where await :: m a instance Monad m => MonadAwait (PlanT i o m) i where {-# INLINE await #-} await = PlanT $ F.wrap $ AwaitPF return (F.liftF StopPF) instance (Monad m, Occasional o) => MonadAwait (Evolution (Event a) o m) a where {-# INLINE await #-} await = Evolution $ cont $ \next -> awaitProc next stopped class MonadYield m a | m -> a where yield :: a -> m () instance Monad m => MonadYield (PlanT i o m) o where {-# INLINE yield #-} yield x = PlanT $ F.liftF $ YieldPF x () instance Monad m => MonadYield (Evolution i (Event a) m) a where {-# INLINE yield #-} yield x = Evolution $ cont $ \next -> yieldProc x (next ()) class MonadStop m where stop :: m a instance Monad m => MonadStop (PlanT i o m) where {-# INLINE stop #-} stop = PlanT $ F.liftF StopPF instance (Monad m, Occasional o) => MonadStop (Evolution i o m) where {-# INLINE stop #-} stop = Evolution $ cont $ const stopped catchP:: Monad m => PlanT i o m a -> PlanT i o m a -> PlanT i o m a catchP (PlanT pl) next0 = PlanT $ F.FT $ \pr free -> F.runFT pl pr (free' next0 pr free) where free' :: Monad m => PlanT i o m a -> (a -> m r) -> (forall x. (x -> m r) -> PlanF i o x -> m r) -> (y -> m r) -> (PlanF i o y) -> m r free' (PlanT next) pr free r pl' = let nextR = F.runFT next pr free go StopPF = nextR go (AwaitPF f ff) = free (either (\_ -> nextR) r) $ AwaitPF (Right . f) (Left ff) go _ = free r pl' in go pl' {-# INLINE awaitProc #-} awaitProc :: (Monad m, Occasional o) => (a -> ProcessT m (Event a) o) -> ProcessT m (Event a) o -> ProcessT m (Event a) o awaitProc f ff = awaitProc' where awaitProc' = ProcessT { paFeed = awaitFeed, paSweep = awaitSweep, paSuspend = const noEvent } awaitFeed (Event x) = feed (f x) NoEvent awaitFeed NoEvent = return (noEvent, awaitProc') awaitFeed End = feed ff End awaitSweep (Event x) = sweep (f x) NoEvent awaitSweep NoEvent = return (Nothing, awaitProc') awaitSweep End = sweep ff End {-# INLINE yieldProc #-} yieldProc :: Monad m => a -> ProcessT m i (Event a) -> ProcessT m i (Event a) yieldProc y pa = ProcessT { paFeed = \_ -> return (Event y, pa), paSweep = \_ -> return (Just (Event y), pa), paSuspend = const NoEvent } {-# INLINE stopped #-} stopped :: (Monad m, Occasional o) => ProcessT m i o stopped = ProcessT { paFeed = \_ -> return (end, arr (const end)), paSweep = \_ -> return (Just end, arr (const end)), paSuspend = pure end } {-# INLINE constructT #-} constructT :: (Monad m) => PlanT i o m r -> ProcessT m (Event i) (Event o) constructT pl0 = runCont (runEvolution $ realizePlan pl0) (const stopped) {-# INLINE realizePlan #-} realizePlan :: Monad m => PlanT i o m a -> Evolution (Event i) (Event o) m a realizePlan pl = Evolution $ cont $ \next -> packProc $ F.runFT (freePlanT pl) (return . next) (\b fr -> return $ free (packProc . b <$> fr)) where free :: Monad m => PlanF i o (ProcessT m (Event i) (Event o)) -> ProcessT m (Event i) (Event o) free (AwaitPF f ff) = awaitProc f ff free (YieldPF y pa) = yieldProc y pa free StopPF = stopped {-# INLINE repeatedlyT #-} repeatedlyT :: Monad m => PlanT i o m r -> ProcessT m (Event i) (Event o) repeatedlyT pl0 = runCont (forever $ runEvolution $ realizePlan pl0) absurd -- for pure {-# INLINE construct #-} construct :: Monad m => PlanT i o Identity r -> ProcessT m (Event i) (Event o) construct = fit (return . runIdentity) . constructT {-# INLINE repeatedly #-} repeatedly :: Monad m => PlanT i o Identity r -> ProcessT m (Event i) (Event o) repeatedly = fit (return . runIdentity) . repeatedlyT -- -- Switches -- -- |Run the 1st transducer at the beggining. Then switch to 2nd when Event t occurs. -- -- >>> :{ -- let -- before = proc x -> -- do -- trigger <- filterEvent (== 3) -< x -- returnA -< ((*10) <$> x, trigger) -- after t = proc x -> returnA -< (*100) <$> x -- in -- run (switch before after) [1..5] -- :} -- [10,20,300,400,500] switch :: Monad m => ProcessT m b (c, Event t) -> (t -> ProcessT m b c) -> ProcessT m b c switch sf k = ggSwitch (const ()) sf (\() -> k) -- |Delayed version of `switch` -- -- >>> :{ -- let -- before = proc x -> -- do -- trigger <- filterEvent (== 3) -< x -- returnA -< ((*10) <$> x, trigger) -- after t = proc x -> returnA -< (*100) <$> x -- in -- run (dSwitch before after) [1..5] -- :} -- [10,20,30,400,500] dSwitch :: Monad m => ProcessT m b (c, Event t) -> (t -> ProcessT m b c) -> ProcessT m b c dSwitch sf k = dggSwitch (const ()) sf (\() -> k) -- |Recurring switch. -- -- >>> :{ -- let pa = proc evtp -> -- do -- evx <- returnA -< fst <$> evtp -- evarr <- filterJust -< snd <$> evtp -- rSwitch (evMap (*10)) -< (evx, evarr) -- l = [(1, Nothing), -- (2, Just (arr $ fmap (*100))), -- (3, Nothing), -- (4, Just (arr $ fmap (*1000))), -- (5, Nothing)] -- in -- run pa l -- :} -- [10,200,300,4000,5000] rSwitch :: Monad m => ProcessT m b c -> ProcessT m (b, Event (ProcessT m b c)) c rSwitch p = rSwitch' (p *** Cat.id) >>> arr fst where rSwitch' pid = kSwitch pid test $ \_ p' -> rSwitch'' (p' *** Cat.id) rSwitch'' pid = dkSwitch pid test $ \s _ -> rSwitch' s test = proc (_, (_, r)) -> returnA -< r -- |Delayed version of `rSwitch`. -- -- >>> :{ -- let pa = proc evtp -> -- do -- evx <- returnA -< fst <$> evtp -- evarr <- filterJust -< snd <$> evtp -- drSwitch (evMap (*10)) -< (evx, evarr) -- l = [(1, Nothing), -- (2, Just (arr $ fmap (*100))), -- (3, Nothing), -- (4, Just (arr $ fmap (*1000))), -- (5, Nothing)] -- in -- run pa l -- :} -- [10,20,300,400,5000] drSwitch :: Monad m => ProcessT m b c -> ProcessT m (b, Event (ProcessT m b c)) c drSwitch p = drSwitch' (p *** Cat.id) where drSwitch' pid = dSwitch pid $ \p' -> drSwitch' (p' *** Cat.id) kSwitch :: Monad m => ProcessT m b c -> ProcessT m (b, c) (Event t) -> (ProcessT m b c -> t -> ProcessT m b c) -> ProcessT m b c kSwitch sf test = ggSwitch (\(CompositeStep _ (CompositeStep (ParStep IDStep sf') _)) -> sf') (CompositeStep (ArrStep (id &&& id)) (CompositeStep (ParStep IDStep sf) (arr snd &&& test))) dkSwitch :: Monad m => ProcessT m b c -> ProcessT m (b, c) (Event t) -> (ProcessT m b c -> t -> ProcessT m b c) -> ProcessT m b c dkSwitch sf test = dggSwitch (\(CompositeStep _ (CompositeStep (ParStep IDStep sf') _)) -> sf') (CompositeStep (ArrStep (id &&& id)) (CompositeStep (ParStep IDStep sf) (arr snd &&& test))) ggSwitch :: (Monad m, Stepper m b (c, Event t) sWhole) => (sWhole -> s) -> sWhole -> (s -> t -> ProcessT m b c) -> ProcessT m b c ggSwitch picker whole k = makePA (\x -> do let (hyevt, whole') <- step whole x let hy = fst <$> hyevt hevt = snd <$> hyevt case (helperToMaybe hevt) of Just (Event t) -> step (k (picker whole') t) x _ -> return (hy, ggSwitch picker whole' k)) (arr fst . suspend whole) dggSwitch :: (Monad m, Stepper m b (c, Event t) sWhole) => (sWhole -> s) -> sWhole -> (s -> t -> ProcessT m b c) -> ProcessT m b c dggSwitch picker whole k = makePA (\x -> do let (hyevt, whole') <- step whole x let hy = fst <$> hyevt hevt = snd <$> hyevt case (helperToMaybe hevt) of Just (Event t) -> return (hy, k (picker whole') t) _ -> return (hy, dggSwitch picker whole' k)) (arr fst . suspend whole) gSwitch :: Monad m => ProcessT m b (p, r) -> ProcessT m p q -> ProcessT m (q, r) (c, Event t) -> (ProcessT m p q -> t -> ProcessT m b c) -> ProcessT m b c gSwitch pre sf post = ggSwitch (\(CompositeStep _ (CompositeStep (ParStep sf' IDStep) _)) -> sf') (CompositeStep pre (CompositeStep (ParStep sf IDStep) post)) dgSwitch :: Monad m => ProcessT m b (p, r) -> ProcessT m p q -> ProcessT m (q, r) (c, Event t) -> (ProcessT m p q -> t -> ProcessT m b c) -> ProcessT m b c dgSwitch pre sf post = dggSwitch (\(CompositeStep _ (CompositeStep (ParStep sf' IDStep) _)) -> sf') (CompositeStep pre (CompositeStep (ParStep sf IDStep) post)) broadcast :: Functor col => b -> col sf -> col (b, sf) broadcast x sfs = fmap (\sf -> (x, sf)) sfs par :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> ProcessT m b (col c) par r sfs = toProcessT (PluralStep r sfs) parB :: (Monad m, Tv.Traversable col) => col (ProcessT m b c) -> ProcessT m b (col c) parB = par broadcast data PluralStep ext col m b c where PluralStep :: (forall sf. (b -> col sf -> col (ext, sf))) -> (col (ProcessT m ext c)) -> PluralStep ext col m b c instance (Monad m, Tv.Traversable col) => Stepper m b (col c) (PluralStep ext col m b c) where feed (PluralStep r sfs) = liftM (runIdentity *** PluralStep r) . parCore r sfs sweep (PluralStep r sfs) = liftM (id *** PluralStep r) . parCore r sfs suspend (PluralStep r sfs) = suspendAll r sfs suspendAll :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> b -> col c suspendAll r sfs = (sus <$>) . (r `flip` sfs) where sus (ext, sf) = suspend sf ext traverseResult :: forall h col c. (Tv.Traversable col, ProcessHelper h) => col (h c, c) -> h (col c) traverseResult zs = let pr :: (h c, c) -> StateT Bool h c pr (hx, d) = do let mx = helperToMaybe hx if isJust mx then put True else return () return (fromMaybe d mx) hxs = runStateT (Tv.sequence (pr <$> zs)) False exist = fromMaybe False $ helperToMaybe (snd <$> hxs) result = fst <$> hxs in if exist then result else join (weakly result) parCore :: (Applicative m, Monad m, Tv.Traversable col, ProcessHelper h) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> b -> m (h (col c), col (ProcessT m ext c)) parCore r sfs x = do let input = r x sfs ret <- Tv.sequenceA $ fmap app' input let zs = traverseResult $ fmap fst ret sfs' = fmap snd ret return (zs, sfs') where app' (y, sf) = do (hz, sf') <- step sf y return ((hz, suspend sf' y), sf') pSwitch :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> ProcessT m (b, col c) (Event mng) -> (col (ProcessT m ext c) -> mng -> ProcessT m b (col c)) -> ProcessT m b (col c) pSwitch r sfs test = ggSwitch (\(CompositeStep _ (CompositeStep (ParStep IDStep (PluralStep _ sfs')) _)) -> sfs') (CompositeStep (ArrStep (id &&& id)) (CompositeStep (ParStep IDStep (PluralStep r sfs)) (arr snd &&& test))) pSwitchB :: (Monad m, Tv.Traversable col) => col (ProcessT m b c) -> ProcessT m (b, col c) (Event mng) -> (col (ProcessT m b c) -> mng -> ProcessT m b (col c)) -> ProcessT m b (col c) pSwitchB = pSwitch broadcast dpSwitch :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> ProcessT m (b, col c) (Event mng) -> (col (ProcessT m ext c) -> mng -> ProcessT m b (col c)) -> ProcessT m b (col c) dpSwitch r sfs test = dggSwitch (\(CompositeStep _ (CompositeStep (ParStep IDStep (PluralStep _ sfs')) _)) -> sfs') (CompositeStep (ArrStep (id &&& id)) (CompositeStep (ParStep IDStep (PluralStep r sfs)) (arr snd &&& test))) dpSwitchB :: (Monad m, Tv.Traversable col) => col (ProcessT m b c) -> ProcessT m (b, col c) (Event mng) -> (col (ProcessT m b c) -> mng -> ProcessT m b (col c)) -> ProcessT m b (col c) dpSwitchB = dpSwitch broadcast rpSwitch :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> ProcessT m (b, Event (col (ProcessT m ext c) -> col (ProcessT m ext c))) (col c) rpSwitch r sfs = ggSwitch (\(ParStep (PluralStep _ sfs') IDStep) -> sfs') (ParStep (PluralStep r sfs) IDStep) (\sfs' tr -> next r (tr sfs')) where next :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> ProcessT m (b, Event (col (ProcessT m ext c) -> col (ProcessT m ext c))) (col c) next r' sfs' = dggSwitch (\(ParStep (PluralStep _ sfs'') IDStep) -> sfs'') (ParStep (PluralStep r' sfs') IDStep) (\sfs'' _ -> rpSwitch r' sfs'') rpSwitchB :: (Monad m, Tv.Traversable col) => col (ProcessT m b c) -> ProcessT m (b, Event (col (ProcessT m b c) -> col (ProcessT m b c))) (col c) rpSwitchB = rpSwitch broadcast drpSwitch :: (Monad m, Tv.Traversable col) => (forall sf. (b -> col sf -> col (ext, sf))) -> col (ProcessT m ext c) -> ProcessT m (b, Event (col (ProcessT m ext c) -> col (ProcessT m ext c))) (col c) drpSwitch r sfs = dggSwitch (\(ParStep (PluralStep _ sfs') IDStep) -> sfs') (ParStep (PluralStep r sfs) IDStep) (\sfs' tr -> drpSwitch r (tr sfs')) drpSwitchB :: (Monad m, Tv.Traversable col) => col (ProcessT m b c) -> ProcessT m (b, Event (col (ProcessT m b c) -> col (ProcessT m b c))) (col c) drpSwitchB = drpSwitch broadcast -- -- Unsafe primitives -- -- | Repeatedly call `p`. -- -- How many times `p` is called is indefinite. -- So `p` must satisfy the equation below; -- -- @p &&& (p >>> arr null) === p &&& arr (const True)@ -- -- where -- -- @null = getAll . foldMap (\_ -> All False)@ unsafeExhaust :: (Monad m, Fd.Foldable f) => (b -> m (f c)) -> ProcessT m b (Event c) unsafeExhaust p = go >>> fork where go = ProcessT { paFeed = \x -> do {y <- p x; return (Event y, go)}, paSweep = \x -> do {y <- p x; return (if nullFd y then Nothing else Just (Event y), go)}, paSuspend = const NoEvent } fork = repeatedly $ await >>= Fd.mapM_ yield nullFd = getAll . Fd.foldMap (\_ -> All False) -- -- Running -- -- -- Running Monad (To be exported) -- data RunInfo i o m = RunInfo { freezeRI :: !(ProcessT m i o), getInputRI :: !i, getPaddingRI :: !i, getPhaseRI :: !Phase } type RM i o m = StateT (RunInfo i o m) m runRM :: Monad m' => ProcessT m (Event i) o -> StateT (RunInfo (Event i) o m) m' x -> m' x runRM pa mx = evalStateT mx $ RunInfo { freezeRI = pa, getInputRI = NoEvent, getPaddingRI = NoEvent, getPhaseRI = Sweep } feed_ :: (Monad m, MonadState (RunInfo i o m') m) => i -> i -> m Bool feed_ input padding = do ph <- gets getPhaseRI if ph == Suspend then do ri <- get put $ ri { getInputRI = input, getPaddingRI = padding, getPhaseRI = Feed } return True else return False feedR :: (Monad m, MonadState (RunInfo (Event i) o m') m) => i -> m Bool feedR x = feed_ (Event x) NoEvent freeze :: Monad m => RM i o m (ProcessT m i o) freeze = gets freezeRI sweepR :: Monad m => RM i o m o sweepR = do pa <- freeze ph <- gets getPhaseRI ri <- get case ph of Feed -> do x <- gets getInputRI (y, pa') <- lift $ feed pa x put $ ri { freezeRI = pa', getPhaseRI = Sweep } return y Sweep -> do x <- gets getPaddingRI (my, pa') <- lift $ sweep pa x put $ ri { freezeRI = pa', getPhaseRI = if isJust my then Sweep else Suspend } return $ fromMaybe (suspend pa x) my Suspend -> do x <- gets getPaddingRI return $ suspend pa x sweepAll :: (Monad m, Monad m') => (forall p. RM i (Event o) m p -> m' p) -> (o -> m' ()) -> ContT Bool m' () sweepAll lft outpre = callCC $ \sus -> forever $ cond sus >> body where cond sus = do ph <- lift $ lft $ gets getPhaseRI if ph == Suspend then sus () else return () body = do evx <- lift $ lft $ sweepR case evx of Event x -> do lift $ outpre x NoEvent -> return () End -> breakCont False breakCont :: Monad m => r -> ContT r m a breakCont = ContT . const . return -- | Run a machine. runT :: (Monad m, Fd.Foldable f) => (c -> m ()) -> ProcessT m (Event b) (Event c) -> f b -> m () runT outpre0 pa0 xs = runRM pa0 $ do _ <- evalContT $ do -- Sweep initial events. sweepAll id outpre -- Feed values Fd.mapM_ feedSweep xs return True -- Terminate. _ <- feed_ End End _ <- evalContT $ sweepAll id outpre >> return True return () where feedSweep x = do _ <- lift $ feedR x sweepAll id outpre outpre = lift . outpre0 type Builder b = F.F ((,) b) putB :: b -> Builder b () putB x = F.liftF (x, ()) bToList :: Builder b a -> [b] bToList x = build $ \cons nil -> F.runF x (const nil) (uncurry cons) -- | Run a machine discarding all results. runT_ :: (Monad m, Fd.Foldable f) => ProcessT m (Event a) (Event b) -> f a -> m () runT_ pa l = runT (const $ return ()) pa l run :: Fd.Foldable f => ProcessT Identity (Event a) (Event b) -> f a -> [b] run pa = bToList . runT putB (fit lift pa) run_ :: (Fd.Foldable f, ArrowApply a) => ProcessA a (Event b) (Event c) -> a (f b) () run_ pa = proc l -> case runT_ pa l of {ArrowMonad f -> f} -<< () lftRM :: (Monad m, Monad m') => (forall p. m p -> m' p) -> RM i o m a -> StateT (RunInfo i o m) m' a lftRM lft' st = StateT $ \s -> lft' $ runStateT st s -- | Execute until an input consumed and the machine suspends. -- -- During the execution, the machine may yield values or stops. -- It can be handled by two callbacks. -- -- In some case the machine failed to consume the input value. -- If so, the value is passed to the termination callback. stepRun :: (Monad m, Monad m') => (forall p. m p -> m' p) -- ^ Lifting function (pass `id` if m' ~ m) -> (b -> m' ()) -- ^ Callback on every output value. -> (Maybe a -> m' ()) -- ^ Callback on termination. -> ProcessT m (Event a) (Event b) -- ^ The machine to run. -> a -- ^ The argument to the machine. -> m' (ProcessT m (Event a) (Event b)) stepRun lft yd stp pa0 x = do pa <- runRM pa0 $ do csmd <- evalContT $ do sweepAll (lftRM lft) (lift . yd) return True if csmd then do ct <- evalContT $ do _ <- lift $ feedR x sweepAll (lftRM lft) (lift . yd) return True if ct then return () else lift $ stp $ Nothing else lift $ stp $ Just x pa <- lftRM lft freeze return pa return pa -- | Execute until an output produced. -- -- During the execution, the machine may await values or stops. -- It can be handled by two callbacks. -- -- If the machine stops without producing any value, -- The first element of the return tuple is `Nothing`. stepYield :: (Monad m, Monad m') => (forall p. m p -> m' p) -- ^ Lifting function (pass `id` if m' ~ m) -> m' a -- ^ Callback on input value request. -> m' () -- ^ Callback on termination -> ProcessT m (Event a) (Event b) -- ^ The machine to run. -> m' (Maybe b, ProcessT m (Event a) (Event b)) stepYield lft aw stp pa0 = runRM pa0 $ do r <- go False pa <- lftRM lft freeze return (r, pa) where go csmd = lftRM lft sweepR >>= handleEv csmd handleEv _ (Event y) = return $ Just y handleEv True NoEvent = return Nothing handleEv False NoEvent = do x <- lift $ aw _ <- lftRM lft $ feedR x go True handleEv _ End = lift stp >> return Nothing