dunai-0.1.0.0: src/Data/MonadicStreamFunction.hs
-- | Monadic Stream Functions are synchronized stream functions
-- with side effects.
module Data.MonadicStreamFunction
( module Control.Arrow
, module Data.MonadicStreamFunction
, module X
)
where
-- External
import Control.Applicative
import Control.Arrow
import Control.Category (Category(..))
import Control.Monad
import Control.Monad.Base
import Data.Monoid
import Prelude hiding ((.), id, sum)
-- Internal (generic)
import Data.VectorSpace
import Data.VectorSpace.Instances()
import Data.MonadicStreamFunction.Core as X
import Data.MonadicStreamFunction.ArrowChoice as X
import Data.MonadicStreamFunction.ArrowLoop as X
import Data.MonadicStreamFunction.ArrowPlus as X
-- ** Instances for monadic streams
instance Functor m => Functor (MStreamF m r)
where
-- fmap f as = as >>> arr f
fmap f as = MStreamF $ \r -> fTuple <$> unMStreamF as r
where
fTuple (a, as') = (f a, f <$> as')
instance Applicative m => Applicative (MStreamF m r) where
-- pure a = constantly a
pure a = MStreamF $ \_ -> pure (a, pure a)
{-
fs <*> as = proc _ -> do
f <- fs -< ()
a <- as -< ()
returnA -< f a
-}
fs <*> as = MStreamF $ \r -> applyTuple <$> unMStreamF fs r <*> unMStreamF as r
where
applyTuple (f, fs') (a, as') = (f a, fs' <*> as')
-- ** Lifts
{-# DEPRECATED insert "Don't use this. liftMStreamF id instead" #-}
insert :: Monad m => MStreamF m (m a) a
insert = liftMStreamF id
-- This expands to the old code:
--
-- MStreamF $ \ma -> do
-- a <- ma
-- return (a, insert)
liftMStreamF_ :: Monad m => m b -> MStreamF m a b
liftMStreamF_ = liftMStreamF . const
-- * Monadic lifting from one monad into another
-- ** Monad stacks
(^>>>) :: MonadBase m1 m2 => MStreamF m1 a b -> MStreamF m2 b c -> MStreamF m2 a c
sf1 ^>>> sf2 = (liftMStreamFBase sf1) >>> sf2
{-# INLINE (^>>>) #-}
(>>>^) :: MonadBase m1 m2 => MStreamF m2 a b -> MStreamF m1 b c -> MStreamF m2 a c
sf1 >>>^ sf2 = sf1 >>> (liftMStreamFBase sf2)
{-# INLINE (>>>^) #-}
-- ** Delays and signal overwriting
-- See also: 'iPre'
iPost :: Monad m => b -> MStreamF m a b -> MStreamF m a b
iPost b sf = MStreamF $ \_ -> return (b, sf)
next :: Monad m => b -> MStreamF m a b -> MStreamF m a b
next b sf = MStreamF $ \a -> do
(b', sf') <- unMStreamF sf a
return (b, next b' sf')
-- rather, once delay is tested:
-- next b sf = sf >>> delay b
-- ** Switching
-- See also: 'switch', and the exception monad combinators for MSFs in
-- Control.Monad.Trans.MStreamF
untilS :: Monad m => MStreamF m a b -> MStreamF m b Bool -> MStreamF m a (b, Maybe ())
untilS sf1 sf2 = sf1 >>> (arr id &&& (sf2 >>> arr boolToMaybe))
where boolToMaybe x = if x then Just () else Nothing
andThen :: Monad m => MStreamF m a (b, Maybe ()) -> MStreamF m a b -> MStreamF m a b
andThen sf1 sf2 = switch sf1 $ const sf2
-- ** Feedback loops
-- | Missing: 'feedback'
-- * Adding side effects
withSideEffect :: Monad m => (a -> m b) -> MStreamF m a a
withSideEffect method = (id &&& liftMStreamF method) >>> arr fst
withSideEffect_ :: Monad m => m b -> MStreamF m a a
withSideEffect_ method = withSideEffect $ const method
-- * Debugging
traceGeneral :: (Monad m, Show a) => (String -> m ()) -> String -> MStreamF m a a
traceGeneral method msg =
withSideEffect (method . (msg ++) . show)
trace :: Show a => String -> MStreamF IO a a
trace = traceGeneral putStrLn
-- FIXME: This does not seem to be a very good name. It should be
-- something like traceWith. It also does too much.
pauseOnGeneral :: (Monad m, Show a) => (a -> Bool) -> (String -> m ()) -> String -> MStreamF m a a
pauseOnGeneral cond method msg = withSideEffect $ \a ->
when (cond a) $ method $ msg ++ show a
pauseOn :: Show a => (a -> Bool) -> String -> MStreamF IO a a
pauseOn cond = pauseOnGeneral cond $ \s -> print s >> getLine >> return ()
-- * Tests and examples
sum :: (Monoid n, Monad m) => MStreamF m n n
sum = sumFrom mempty
{-# INLINE sum #-}
sumFrom :: (Monoid n, Monad m) => n -> MStreamF m n n
sumFrom n0 = MStreamF $ \n -> let acc = n0 `mappend` n
-- in acc `seq` return (acc, sumFrom acc)
in return (acc, sumFrom acc)
-- sum = feedback 0 (arr (uncurry (+) >>> dup))
-- where dup x = (x,x)
count :: (Num n, Monad m) => MStreamF m () n
count = arr (const (Sum 1)) >>> sum >>> arr getSum
unfold :: Monad m => (a -> (b,a)) -> a -> MStreamF m () b
unfold f a = MStreamF $ \_ -> let (b,a') = f a in b `seq` return (b, unfold f a')
-- unfold f x = feedback x (arr (snd >>> f))
repeatedly :: Monad m => (a -> a) -> a -> MStreamF m () a
repeatedly f = repeatedly'
where repeatedly' a = MStreamF $ \() -> let a' = f a in a' `seq` return (a, repeatedly' a')
-- repeatedly f x = feedback x (arr (f >>> \x -> (x,x)))
-- FIXME: This should *not* be in this module
mapMStreamF :: Monad m => MStreamF m a b -> MStreamF m [a] [b]
mapMStreamF sf = MStreamF $ consume sf
where
consume :: Monad m => MStreamF m a t -> [a] -> m ([t], MStreamF m [a] [t])
consume sf [] = return ([], mapMStreamF sf)
consume sf (a:as) = do
(b, sf') <- unMStreamF sf a
(bs, sf'') <- consume sf' as
b `seq` return (b:bs, sf'')
-- * Streams (or generators)
type MStream m a = MStreamF m () a