dunai 0.4.0.0 → 0.5
raw patch · 27 files changed
+800/−962 lines, 27 files
Files
- dunai.cabal +5/−5
- src/Control/Arrow/Util.hs +0/−12
- src/Control/Monad/Trans/MSF.hs +1/−3
- src/Control/Monad/Trans/MSF/Except.hs +81/−36
- src/Control/Monad/Trans/MSF/GenLift.hs +0/−138
- src/Control/Monad/Trans/MSF/Maybe.hs +9/−36
- src/Control/Monad/Trans/MSF/RWS.hs +37/−0
- src/Control/Monad/Trans/MSF/Random.hs +39/−15
- src/Control/Monad/Trans/MSF/Reader.hs +14/−83
- src/Control/Monad/Trans/MSF/State.hs +32/−107
- src/Control/Monad/Trans/MSF/Writer.hs +21/−79
- src/Data/MonadicStreamFunction.hs +10/−9
- src/Data/MonadicStreamFunction/Async.hs +2/−1
- src/Data/MonadicStreamFunction/Core.hs +123/−163
- src/Data/MonadicStreamFunction/Instances/ArrowChoice.hs +1/−0
- src/Data/MonadicStreamFunction/Instances/ArrowLoop.hs +2/−1
- src/Data/MonadicStreamFunction/Instances/ArrowPlus.hs +8/−0
- src/Data/MonadicStreamFunction/Instances/Num.hs +6/−5
- src/Data/MonadicStreamFunction/Instances/VectorSpace.hs +4/−4
- src/Data/MonadicStreamFunction/InternalCore.hs +135/−0
- src/Data/MonadicStreamFunction/Parallel.hs +4/−3
- src/Data/MonadicStreamFunction/ReactHandle.hs +9/−19
- src/Data/MonadicStreamFunction/Util.hs +37/−44
- src/Data/VectorSpace.hs +220/−16
- src/Data/VectorSpace/Fractional.hs +0/−37
- src/Data/VectorSpace/Specific.hs +0/−48
- src/Data/VectorSpace/Tuples.hs +0/−98
dunai.cabal view
@@ -1,5 +1,5 @@ name: dunai-version: 0.4.0.0+version: 0.5 synopsis: Generalised reactive framework supporting classic, arrowized and monadic FRP. homepage: https://github.com/ivanperez-keera/dunai description:@@ -56,10 +56,10 @@ library exposed-modules: Control.Monad.Trans.MSF Control.Monad.Trans.MSF.Except- Control.Monad.Trans.MSF.GenLift Control.Monad.Trans.MSF.Maybe Control.Monad.Trans.MSF.Random Control.Monad.Trans.MSF.Reader+ Control.Monad.Trans.MSF.RWS Control.Monad.Trans.MSF.State Control.Monad.Trans.MSF.Writer Data.MonadicStreamFunction@@ -70,15 +70,13 @@ Data.MonadicStreamFunction.Instances.ArrowPlus Data.MonadicStreamFunction.Instances.Num Data.MonadicStreamFunction.Instances.VectorSpace+ Data.MonadicStreamFunction.InternalCore Data.MonadicStreamFunction.Parallel Data.MonadicStreamFunction.ReactHandle Data.MonadicStreamFunction.Util -- Auxiliary definitions Data.VectorSpace- Data.VectorSpace.Fractional- Data.VectorSpace.Tuples- Data.VectorSpace.Specific other-modules: Control.Arrow.Util @@ -94,6 +92,7 @@ type: exitcode-stdio-1.0 main-is: hlint.hs hs-source-dirs: tests+ default-language: Haskell2010 if !flag(test-hlint) buildable: False else@@ -107,6 +106,7 @@ main-is: HaddockCoverage.hs ghc-options: -Wall hs-source-dirs: tests+ default-language: Haskell2010 if !flag(test-doc-coverage) buildable: False
src/Control/Arrow/Util.hs view
@@ -8,18 +8,6 @@ constantly = arr . const {-# INLINE constantly #-} --- | Alternative implementation of '<<<' that binds more strongly.-infixr 4 <-<-(<-<) :: Arrow a => a c d -> a b c -> a b d-(<-<) = (<<<)-{-# INLINE (<-<) #-}---- | Alternative implementation of '>>>' that binds more strongly.-infixr 4 >->-(>->) :: Arrow a => a b c -> a c d -> a b d-(>->) = (>>>)-{-# INLINE (>->) #-}- -- import Control.Category (id) -- import Prelude hiding (id)
src/Control/Monad/Trans/MSF.hs view
@@ -3,8 +3,7 @@ -- | This module reexports nearly all submodules. RWS is not exported since -- names collide with Reader, State and Writer. module Control.Monad.Trans.MSF- ( module Control.Monad.Trans.MSF.GenLift- , module Control.Monad.Trans.MSF.Except+ ( module Control.Monad.Trans.MSF.Except , module Control.Monad.Trans.MSF.Maybe , module Control.Monad.Trans.MSF.Random , module Control.Monad.Trans.MSF.Reader@@ -13,7 +12,6 @@ ) where -import Control.Monad.Trans.MSF.GenLift import Control.Monad.Trans.MSF.Except import Control.Monad.Trans.MSF.Maybe import Control.Monad.Trans.MSF.Random
src/Control/Monad/Trans/MSF/Except.hs view
@@ -20,8 +20,8 @@ import Control.Monad.Trans.Maybe -- Internal--- import Control.Monad.Trans.MSF.GenLift import Data.MonadicStreamFunction+import Data.MonadicStreamFunction.InternalCore -- * Throwing exceptions @@ -50,7 +50,7 @@ then throwS -< e else returnA -< () --- | When the input is 'Just e', throw the exception 'e'.+-- | When the input is @Just e@, throw the exception @e@. -- (Does not output any actual data.) throwMaybe :: Monad m => MSF (ExceptT e m) (Maybe e) (Maybe a) throwMaybe = mapMaybeS throwS@@ -61,15 +61,17 @@ -- | Immediately throw the given exception. throw :: Monad m => e -> MSF (ExceptT e m) a b-throw = arrM_ . throwE+throw = constM . throwE -- | Do not throw an exception. pass :: Monad m => MSF (ExceptT e m) a a pass = Category.id --- | Whenever 'Nothing' is thrown, throw '()' instead.-maybeToExceptS :: (Functor m, Monad m) => MSF (MaybeT m) a b -> MSF (ExceptT () m) a b-maybeToExceptS = liftMSFPurer (ExceptT . (maybe (Left ()) Right <$>) . runMaybeT)+-- | Converts an 'MSF' in 'MaybeT' to an 'MSF' in 'ExceptT'.+-- Whenever 'Nothing' is thrown, throw @()@ instead.+maybeToExceptS :: (Functor m, Monad m)+ => MSF (MaybeT m) a b -> MSF (ExceptT () m) a b+maybeToExceptS = morphS (ExceptT . (maybe (Left ()) Right <$>) . runMaybeT) -- * Catching exceptions @@ -83,25 +85,24 @@ e <- try msf safe $ f e --- | Similar to Yampa's delayed switching. Looses a 'b' in case of an exception.+-- | Similar to Yampa's delayed switching. Loses a @b@ in case of an exception. untilE :: Monad m => MSF m a b -> MSF m b (Maybe e) -> MSF (ExceptT e m) a b untilE msf msfe = proc a -> do- b <- liftMSFTrans msf -< a- me <- liftMSFTrans msfe -< b+ b <- liftTransS msf -< a+ me <- liftTransS msfe -< b inExceptT -< ExceptT $ return $ maybe (Right b) Left me +-- TODO This needs to be renamed as 'runExceptS'!+-- 'exceptS' would have type @MSF m a (Either e b) -> MSF (ExceptT e m) a b@ -- | Escape an 'ExceptT' layer by outputting the exception whenever it occurs. -- If an exception occurs, the current 'MSF' continuation is tested again -- on the next input.-exceptS :: Monad m => MSF (ExceptT e m) a b -> MSF m a (Either e b)-exceptS msf = go- where- go = MSF $ \a -> do- cont <- runExceptT $ unMSF msf a- case cont of- Left e -> return (Left e, go)- Right (b, msf') -> return (Right b, exceptS msf')+exceptS :: (Functor m, Monad m) => MSF (ExceptT e m) a b -> MSF m a (Either e b)+exceptS = transG return $ const $ fmap f . runExceptT+ where+ f (Left e) = (Left e , Nothing)+ f (Right (b, c)) = (Right b, Just c ) -- | Embed an 'ExceptT' value inside the 'MSF'. -- Whenever the input value is an ordinary value,@@ -113,7 +114,7 @@ -- replace the exception by the second component of the tuple. tagged :: Monad m => MSF (ExceptT e1 m) a b -> MSF (ExceptT e2 m) (a, e2) b tagged msf = runMSFExcept $ do- e1 <- try $ msf <<< arr fst+ _ <- try $ msf <<< arr fst (_, e2) <- currentInput return e2 @@ -124,15 +125,19 @@ -- are in fact monads /in the exception type/. -- -- * 'return' corresponds to throwing an exception immediately.--- * '(>>=)' is exception handling:+-- * '>>=' is exception handling: -- The first value throws an exception, -- while the Kleisli arrow handles the exception -- and produces a new signal function, -- which can throw exceptions in a different type.+-- * @m@: The monad that the 'MSF' may take side effects in.+-- * @a@: The input type+-- * @b@: The output type+-- * @e@: The type of exceptions that can be thrown newtype MSFExcept m a b e = MSFExcept { runMSFExcept :: MSF (ExceptT e m) a b } --- | An alias for the |MSFExcept| constructor,--- used to enter the |MSFExcept| monad context.+-- | An alias for the 'MSFExcept' constructor,+-- used to enter the 'MSFExcept' monad context. -- Execute an 'MSF' in 'ExceptT' until it raises an exception. try :: MSF (ExceptT e m) a b -> MSFExcept m a b e try = MSFExcept@@ -152,30 +157,41 @@ pure = MSFExcept . throw (<*>) = ap --- | Monad instance for 'MSFExcept'. Bind uses the exception as the "return"+-- | Monad instance for 'MSFExcept'. Bind uses the exception as the 'return' -- value in the monad. instance Monad m => Monad (MSFExcept m a b) where- MSFExcept msf >>= f = MSFExcept $ MSF $ \a -> do- cont <- lift $ runExceptT $ unMSF msf a- case cont of- Left e -> unMSF (runMSFExcept $ f e) a- Right (b, msf') -> return (b, runMSFExcept $ try msf' >>= f)+ MSFExcept msf >>= f = MSFExcept $ handleExceptT msf $ runMSFExcept . f +handleExceptT+ :: Monad m+ => MSF (ExceptT e1 m) a b+ -> (e1 -> MSF (ExceptT e2 m) a b)+ -> MSF (ExceptT e2 m) a b+handleExceptT msf f = flip handleGen msf $ \a mbcont -> do+ ebcont <- lift $ runExceptT mbcont+ case ebcont of+ Left e -> unMSF (f e) a+ Right (b, msf') -> return (b, handleExceptT msf' f)+++ -- | The empty type. As an exception type, it encodes "no exception possible". data Empty -- | If no exception can occur, the 'MSF' can be executed without the 'ExceptT' layer. safely :: Monad m => MSFExcept m a b Empty -> MSF m a b-safely (MSFExcept msf) = safely' msf+safely (MSFExcept msf) = morphS fromExcept msf where- safely' msf = MSF $ \a -> do- Right (b, msf') <- runExceptT $ unMSF msf a- return (b, safely' msf')+ fromExcept ma = do+ -- We can assume that the pattern @Left e@ will not occur,+ -- since @e@ would have to be of type @Empty@.+ Right a <- runExceptT ma+ return a -- | An 'MSF' without an 'ExceptT' layer never throws an exception, -- and can thus have an arbitrary exception type. safe :: Monad m => MSF m a b -> MSFExcept m a b e-safe = try . liftMSFTrans+safe = try . liftTransS -- | Inside the 'MSFExcept' monad, execute an action of the wrapped monad. -- This passes the last input value to the action,@@ -203,10 +219,10 @@ -- that could only be broken by moving a few things to Data.MonadicStreamFunction.Core -- (that probably belong there anyways). --- | Extract MSF from a monadic action.+-- | Extract an 'MSF' from a monadic action. ----- Runs a monadic action that produces an MSF on the first iteration/step, and--- uses that MSF as the main signal function for all inputs (including the+-- Runs a monadic action that produces an 'MSF' on the first iteration/step, and+-- uses that 'MSF' as the main signal function for all inputs (including the -- first one). performOnFirstSample :: Monad m => m (MSF m a b) -> MSF m a b performOnFirstSample sfaction = safely $ do@@ -221,4 +237,33 @@ -- | Reactimates an 'MSF' until it returns 'True'. reactimateB :: Monad m => MSF m () Bool -> m ()-reactimateB sf = reactimateExcept $ try $ liftMSFTrans sf >>> throwOn ()+reactimateB sf = reactimateExcept $ try $ liftTransS sf >>> throwOn ()++-- * Analog to Yampa's switch, with Maybe instead of Event+switch :: Monad m => MSF m a (b, Maybe c) -> (c -> MSF m a b) -> MSF m a b+switch sf f = catchS ef f+ where+ ef = proc a -> do+ (b,me) <- liftTransS sf -< a+ inExceptT -< ExceptT $ return $ maybe (Right b) Left me++-- | More general lifting combinator that enables recovery. Note that, unlike a+-- polymorphic lifting function @forall a . m a -> m1 a@, this auxiliary+-- function needs to be a bit more structured, and produces a Maybe value. The+-- previous 'MSF' is used if a new one is not produced.+transG :: (Monad m1, Monad m2)+ => (a2 -> m1 a1)+ -> (forall c. a2 -> m1 (b1, c) -> m2 (b2, Maybe c))+ -> MSF m1 a1 b1+ -> MSF m2 a2 b2+transG transformInput transformOutput msf = go+ where go = MSF $ \a2 -> do+ (b2, msf') <- transformOutput a2 $ unMSF msf =<< transformInput a2+ case msf' of+ Just msf'' -> return (b2, transG transformInput transformOutput msf'')+ Nothing -> return (b2, go)++handleGen :: (a -> m1 (b1, MSF m1 a b1) -> m2 (b2, MSF m2 a b2))+ -> MSF m1 a b1+ -> MSF m2 a b2+handleGen handler msf = MSF $ \a -> handler a (unMSF msf a)
− src/Control/Monad/Trans/MSF/GenLift.hs
@@ -1,138 +0,0 @@-{-# LANGUAGE Rank2Types #-}---- | More generic lifting combinators.------ This module contains more generic lifting combinators. It includes several--- implementations, and obviously should be considered work in progress. The--- goal is to make this both simple and conceptually understandable.-module Control.Monad.Trans.MSF.GenLift where--import Control.Applicative-import Data.MonadicStreamFunction---- | Lifting combinator to move from one monad to another, if one has a--- function to run computations in one monad into another. Note that, unlike a--- polymorphic lifting function @forall a . m a -> m1 a@, this auxiliary--- function needs to be a bit more structured.---- Attempt at writing a more generic MSF lifting combinator. This is--- here only to make it easier to find, in a perfect world we'd move--- this to a different module/branch, or at least to the bottom of the--- file.------ TODO: does this also work well with the state and the writer monads?------ Even if this code works, it's difficult to understand the concept.------ It is also unclear how much it helps. Ideally, the auxiliary function--- should operate only on monadic values, not monadic stream functions.--- That way we could separate concepts: namely the recursion pattern--- from the monadic lifting/unlifting/sequencing.------ Maybe if we split f in several functions, one that does some sort of--- a -> a1 transformation, another that does some b1 -> b--- transformation, with the monads and continuations somewhere, it'll--- make more sense.------ Based on this lifting function we can also defined all the other--- liftings we have in Core:------ liftMSFPurer' :: (Monad m1, Monad m)--- => (m1 (b, MSF m1 a b) -> m (b, MSF m1 a b))--- -> MSF m1 a b--- -> MSF m a b--- liftMSFPurer' f = lifterS (\g a -> f $ g a)------ More liftings:--- liftMSFTrans = liftMSFPurer lift--- liftMSFBase = liftMSFPurer liftBase------ And a strict version of liftMSFPurer:--- liftMStreamPurer' f = liftMSFPurer (f >=> whnfVal)--- where whnfVal p@(b,_) = b `seq` return p------ MB: I'm not sure we're gaining much insight by rewriting all the lifting--- functions like that.--- IP: I said the same thing above ("It is also unclear how much it--- helps."). It's work in progress.------ MB: The type (a1 -> m1 (b1, MSF m1 a1 b1)) is just MSF m1 a1 b1.--- IP: I'm looking for a lifting pattern in terms of m m1 a b a1 and b1. By--- exposing the function, I'm hoping to *eventually see* the pattern. If I hide--- it in the MSF, then it'll always remain hidden.-lifterS :: (Monad m, Monad m1)- => ((a1 -> m1 (b1, MSF m1 a1 b1)) -> a -> m (b, MSF m1 a1 b1))- -> MSF m1 a1 b1- -> MSF m a b-lifterS f msf = MSF $ \a -> do- (b, msf') <- f (unMSF msf) a- return (b, lifterS f msf')---- | Lifting combinator to move from one monad to another, if one has a--- function to run computations in one monad into another. Note that, unlike a--- polymorphic lifting function @forall a . m a -> m1 a@, this auxiliary--- function needs to be a bit more structured, although less structured than--- 'lifterS'.--transS :: (Monad m1, Monad m2)- => (a2 -> m1 a1)- -> (forall c. a2 -> m1 (b1, c) -> m2 (b2, c))- -> MSF m1 a1 b1 -> MSF m2 a2 b2-transS transformInput transformOutput msf = MSF $ \a2 -> do- (b2, msf') <- transformOutput a2 $ unMSF msf =<< transformInput a2- return (b2, transS transformInput transformOutput msf')---- | Lifting combinator to move from one monad to another, if one has a--- function to run computations in one monad into another. Note that, unlike a--- polymorphic lifting function @forall a . m a -> m1 a@, this auxiliary--- function needs to be a bit more structured, although less structured than--- 'lifterS'.-transG1 :: (Monad m1, Functor m2, Monad m2)- => (a2 -> m1 a1)- -> (forall c. a2 -> m1 (b1, c) -> m2 (b2, c))- -> MSF m1 a1 b1 -> MSF m2 a2 b2-transG1 transformInput transformOutput msf =- transG transformInput transformOutput' msf- where- -- transformOutput' :: forall c. a2 -> m1 (b1, c) -> m2 (b2, Maybe c)- transformOutput' a b = second Just <$> transformOutput a b---- | More general lifting combinator that enables recovery. Note that, unlike a--- polymorphic lifting function @forall a . m a -> m1 a@, this auxiliary--- function needs to be a bit more structured, and produces a Maybe value. The--- previous MSF is used if a new one is not produced.-transG :: (Monad m1, Monad m2)- => (a2 -> m1 a1)- -> (forall c. a2 -> m1 (b1, c) -> m2 (b2, Maybe c))- -> MSF m1 a1 b1 -> MSF m2 a2 b2-transG transformInput transformOutput msf = go- where go = MSF $ \a2 -> do- (b2, msf') <- transformOutput a2 $ unMSF msf =<< transformInput a2- case msf' of- Just msf'' -> return (b2, transG transformInput transformOutput msf'')- Nothing -> return (b2, go)---- transGN :: (Monad m1, Monad m2)--- => (a2 -> m1 a1)--- -> (forall c. a2 -> m1 (b1, c) -> m2 (b2, [c]))--- -> MSF m1 a1 b1 -> MSF m2 a2 b2--- transGN transformInput transformOutput msf = go--- where go = MSF $ \a2 -> do--- (b2, msf') <- transformOutput a2 $ unMSF msf =<< transformInput a2--- case msf' of--- [] -> return (b2, go)--- [msf''] -> return (b2, transGN transformInput transformOutput msf'')--- ms ->---- IP: Alternative formulation (typechecks just fine):------ FIXME: The foralls may get in the way. They may not be necessary. MB--- raised the issue already for similar code in Core.------ type Wrapper m1 m2 t1 t2 = forall a b . (t1 a -> m2 b ) -> (a -> m1 (t2 b))--- type Unwrapper m1 m2 t1 t2 = forall a b . (a -> m1 (t2 b)) -> (t1 a -> m2 b )------ Helper type, for when we need some identity * -> * type constructor that--- does not get in the way.------ type Id a = a
src/Control/Monad/Trans/MSF/Maybe.hs view
@@ -17,14 +17,13 @@ -- Internal import Control.Monad.Trans.MSF.Except-import Control.Monad.Trans.MSF.GenLift import Data.MonadicStreamFunction -- * Throwing 'Nothing' as an exception ("exiting") -- | Throw the exception immediately. exit :: Monad m => MSF (MaybeT m) a b-exit = arrM_ $ MaybeT $ return Nothing+exit = constM $ MaybeT $ return Nothing -- | Throw the exception when the condition becomes true on the input. exitWhen :: Monad m => (a -> Bool) -> MSF (MaybeT m) a a@@ -52,8 +51,8 @@ -- | Run the first @msf@ until the second one produces 'True' from the output of the first. untilMaybe :: Monad m => MSF m a b -> MSF m b Bool -> MSF (MaybeT m) a b untilMaybe msf cond = proc a -> do- b <- liftMSFTrans msf -< a- c <- liftMSFTrans cond -< b+ b <- liftTransS msf -< a+ c <- liftTransS cond -< b inMaybeT -< if c then Nothing else Just b -- | When an exception occurs in the first 'msf', the second 'msf' is executed from there.@@ -75,47 +74,21 @@ -- * Running 'MaybeT' -- | Remove the 'MaybeT' layer by outputting 'Nothing' when the exception occurs. -- The continuation in which the exception occurred is then tested on the next input.-runMaybeS :: Monad m => MSF (MaybeT m) a b -> MSF m a (Maybe b)-runMaybeS msf = go+runMaybeS :: (Functor m, Monad m) => MSF (MaybeT m) a b -> MSF m a (Maybe b)+runMaybeS msf = exceptS (maybeToExceptS msf) >>> arr eitherToMaybe where- go = MSF $ \a -> do- bmsf <- runMaybeT $ unMSF msf a- case bmsf of- Just (b, msf') -> return (Just b, runMaybeS msf')- Nothing -> return (Nothing, go)+ eitherToMaybe (Left ()) = Nothing+ eitherToMaybe (Right b) = Just b --- | Different implementation, to study performance.-runMaybeS'' :: Monad m => MSF (MaybeT m) a b -> MSF m a (Maybe b)-runMaybeS'' = transG transformInput transformOutput- where- transformInput = return- transformOutput _ m1 = do r <- runMaybeT m1- case r of- Nothing -> return (Nothing, Nothing)- Just (b, c) -> return (Just b, Just c) --- mapMaybeS msf == runMaybeS (inMaybeT >>> lift mapMaybeS)--{--runMaybeS'' :: Monad m => MSF (MaybeT m) a b -> MSF m a (Maybe b)-runMaybeS'' msf = transS transformInput transformOutput msf- where- transformInput = return- transformOutput _ msfaction = do- thing <- runMaybeT msfaction- case thing of- Just (b, msf') -> return (Just b, msf')- Nothing -> return (Nothing, msf)--}- -- | Reactimates an 'MSF' in the 'MaybeT' monad until it throws 'Nothing'. reactimateMaybe :: (Functor m, Monad m) => MSF (MaybeT m) () () -> m () reactimateMaybe msf = reactimateExcept $ try $ maybeToExceptS msf --- | Run an MSF fed from a list, discarding results. Useful when one needs to+-- | Run an 'MSF' fed from a list, discarding results. Useful when one needs to -- combine effects and streams (i.e., for testing purposes). embed_ :: (Functor m, Monad m) => MSF m a () -> [a] -> m () -embed_ msf as = reactimateMaybe $ listToMaybeS as >>> liftMSFTrans msf+embed_ msf as = reactimateMaybe $ listToMaybeS as >>> liftTransS msf
+ src/Control/Monad/Trans/MSF/RWS.hs view
@@ -0,0 +1,37 @@+-- | This module combines the wrapping and running functions+-- for the 'Reader', 'Writer' and 'State' monad layers in a single layer.+--+-- It is based on the _strict_ 'RWS' monad 'Control.Monad.Trans.RWS.Strict',+-- so when combining it with other modules such as @mtl@'s,+-- the strict version has to be included, i.e. 'Control.Monad.RWS.Strict'+-- instead of 'Control.Monad.RWS' or 'Control.Monad.RWS.Lazy'.+module Control.Monad.Trans.MSF.RWS+ ( module Control.Monad.Trans.MSF.RWS+ , module Control.Monad.Trans.RWS.Strict+ ) where++-- External+import Control.Monad.Trans.RWS.Strict+ hiding (liftCallCC, liftCatch) -- Avoid conflicting exports+import Data.Monoid+import Data.Functor ((<$>))++-- Internal+import Data.MonadicStreamFunction++-- * 'RWS' (Reader-Writer-State) monad+++-- | Wrap an 'MSF' with explicit state variables in 'RWST' monad.+rwsS :: (Functor m, Monad m, Monoid w)+ => MSF m (r, s, a) (w, s, b)+ -> MSF (RWST r w s m) a b+rwsS = morphGS $ \f a -> RWST $ \r s -> (\((w, s', b), c) -> ((b, c), s', w))+ <$> f (r, s, a)++-- | Run the 'RWST' layer by making the state variables explicit.+runRWSS :: (Functor m, Monad m, Monoid w)+ => MSF (RWST r w s m) a b+ -> MSF m (r, s, a) (w, s, b)+runRWSS = morphGS $ \f (r, s, a) -> (\((b, c), s', w) -> ((w, s', b), c))+ <$> runRWST (f a) r s
src/Control/Monad/Trans/MSF/Random.hs view
@@ -1,5 +1,15 @@+-- | In this module, 'MSF's in a monad supporting random number generation+-- (i.e. having the 'RandT' layer in its stack) can be run.+-- Running means supplying an initial random number generator,+-- where the update of the generator at every random number generation+-- is already taken care of.+--+-- Under the hood, 'RandT' is basically just 'StateT',+-- with the current random number generator as mutable state.++ {-# LANGUAGE Arrows #-}-module Control.Monad.Trans.MSF.Random +module Control.Monad.Trans.MSF.Random ( runRandS , evalRandS@@ -17,34 +27,48 @@ -- Internal import Data.MonadicStreamFunction+import Control.Monad.Trans.MSF.State --- | Updates the generator every step-runRandS :: (RandomGen g, Monad m) - => MSF (RandT g m) a b - -> g +-- | Run an 'MSF' in the 'RandT' random number monad transformer+-- by supplying an initial random generator.+-- Updates the generator every step.+runRandS :: (RandomGen g, Functor m, Monad m)+ => MSF (RandT g m) a b+ -> g -- ^ The initial random number generator. -> MSF m a (g, b)-runRandS msf g = MSF $ \a -> do- ((b, msf'), g') <- runRandT (unMSF msf a) g- return ((g', b), runRandS msf' g')+runRandS = runStateS_ . morphS (StateT . runRandT) --- | Updates the generator every step but discharges the generator -evalRandS :: (RandomGen g, Monad m) => MSF (RandT g m) a b -> g -> MSF m a b+-- | Evaluate an 'MSF' in the 'RandT' transformer,+-- i.e. extract possibly random values+-- by supplying an initial random generator.+-- Updates the generator every step but discharges the generator.+evalRandS :: (RandomGen g, Functor m, Monad m)+ => MSF (RandT g m) a b -> g -> MSF m a b evalRandS msf g = runRandS msf g >>> arr snd +-- | Create a stream of random values. getRandomS :: (MonadRandom m, Random b) => MSF m a b-getRandomS = arrM_ getRandom+getRandomS = constM getRandom ++-- | Create a stream of lists of random values. getRandomsS :: (MonadRandom m, Random b) => MSF m a [b]-getRandomsS = arrM_ getRandoms +getRandomsS = constM getRandoms +-- | Create a stream of random values in a given fixed range. getRandomRS :: (MonadRandom m, Random b) => (b, b) -> MSF m a b-getRandomRS range = arrM_ $ getRandomR range+getRandomRS range = constM $ getRandomR range +-- | Create a stream of random values in a given range,+-- where the range is specified on every tick. getRandomRS_ :: (MonadRandom m, Random b) => MSF m (b, b) b getRandomRS_ = arrM getRandomR- ++-- | Create a stream of lists of random values in a given fixed range. getRandomsRS :: (MonadRandom m, Random b) => (b, b) -> MSF m a [b]-getRandomsRS range = arrM_ $ getRandomRs range +getRandomsRS range = constM $ getRandomRs range +-- | Create a stream of lists of random values in a given range,+-- where the range is specified on every tick. getRandomsRS_ :: (MonadRandom m, Random b) => MSF m (b, b) [b] getRandomsRS_ = arrM getRandomRs
src/Control/Monad/Trans/MSF/Reader.hs view
@@ -1,22 +1,17 @@ {-# LANGUAGE Rank2Types #-} --- | MSFs with a Reader monadic layer.+-- | 'MSF's with a 'Reader' monadic layer. ----- This module contains functions to work with MSFs that include a 'Reader'--- monadic layer. This includes functions to create new MSFs that include an--- additional layer, and functions to flatten that layer out of the MSF's+-- This module contains functions to work with 'MSF's that include a 'Reader'+-- monadic layer. This includes functions to create new 'MSF's that include an+-- additional layer, and functions to flatten that layer out of the 'MSF`'s -- transformer stack. module Control.Monad.Trans.MSF.Reader ( module Control.Monad.Trans.Reader- -- * Reader MSF wrapping/unwrapping.+ -- * 'Reader' 'MSF' running and wrapping. , readerS , runReaderS , runReaderS_- -- ** Alternative implementation using internal type.- , readerS'- , runReaderS'- -- ** Alternative implementation using generic lifting.- , runReaderS'' ) where -- External@@ -24,86 +19,22 @@ hiding (liftCallCC, liftCatch) -- Avoid conflicting exports -- Internal-import Control.Monad.Trans.MSF.GenLift import Data.MonadicStreamFunction --- * Reader MSF wrapping/unwrapping+-- * Reader 'MSF' running and wrapping --- | Build an MSF in the 'Reader' monad from one that takes the reader+-- | Build an 'MSF' in the 'Reader' monad from one that takes the reader -- environment as an extra input. This is the opposite of 'runReaderS'.-readerS :: Monad m => MSF m (s, a) b -> MSF (ReaderT s m) a b-readerS msf = MSF $ \a -> do- (b, msf') <- ReaderT $ \s -> unMSF msf (s, a)- return (b, readerS msf')+readerS :: Monad m => MSF m (r, a) b -> MSF (ReaderT r m) a b+readerS = morphGS $ \f a -> ReaderT $ \r -> f (r, a) --- | Build an MSF that takes an environment as an extra input from one on the+-- | Build an 'MSF' that takes an environment as an extra input from one on the -- 'Reader' monad. This is the opposite of 'readerS'.-runReaderS :: Monad m => MSF (ReaderT s m) a b -> MSF m (s, a) b-runReaderS msf = MSF $ \(s,a) -> do- (b, msf') <- runReaderT (unMSF msf a) s- return (b, runReaderS msf')+runReaderS :: Monad m => MSF (ReaderT r m) a b -> MSF m (r, a) b+runReaderS = morphGS $ \f (r, a) -> runReaderT (f a) r --- | Build an MSF /function/ that takes a fixed environment as additional+-- | Build an 'MSF' /function/ that takes a fixed environment as additional -- input, from an MSF in the 'Reader' monad.------ This should be always equal to:------ @--- runReaderS_ msf s = arr (\a -> (s,a)) >>> runReaderS msf--- @------ although possibly more efficient.- runReaderS_ :: Monad m => MSF (ReaderT s m) a b -> s -> MSF m a b-runReaderS_ msf s = MSF $ \a -> do- (b, msf') <- runReaderT (unMSF msf a) s- return (b, runReaderS_ msf' s)---- ** Alternative implementation using internal type.---- TODO: One one should exist, ideally.---- | Alternative version of 'readerS'.-readerS' :: Monad m => MSF m (s, a) b -> MSF (ReaderT s m) a b-readerS' = lifterS wrapReaderT---- | Alternative version of 'runReaderS' wrapping/unwrapping functions.-runReaderS' :: Monad m => MSF (ReaderT s m) a b -> MSF m (s, a) b-runReaderS' = lifterS unwrapReaderT--wrapReaderT :: ((s, a) -> m b) -> a -> ReaderT s m b-wrapReaderT g i = ReaderT $ g . flip (,) i--unwrapReaderT :: (a -> ReaderT s m b) -> (s, a) -> m b-unwrapReaderT g i = uncurry (flip runReaderT) $ second g i---- ** Alternative implementation using generic lifting.---- | Alternative version of 'runReaderS'.-runReaderS'' :: Monad m => MSF (ReaderT s m) a b -> MSF m (s, a) b-runReaderS'' = transG transformInput transformOutput- where- transformInput (_, a) = return a- transformOutput (s, _) m1 = do (r, c) <- runReaderT m1 s- return (r, Just c)--{--readerS'' :: Monad m => MSF m (s, a) b -> MSF (ReaderT s m) a b-readerS'' = transS transformInput transformOutput- where- transformInput :: a -> m (s, a)- transformInput a = (,) <$> asks <*> pure a- transformOutput _ = lift--}----- Another alternative:------ type ReaderWrapper s m = Wrapper (ReaderT s m) m ((,) s) Id--- type ReaderUnwrapper s m = Unwrapper (ReaderT s m) m ((,) s) Id------ and use the types:------ wrapReaderT :: ReaderWrapper s m--- unwrapReaderT :: ReaderUnwrapper s m+runReaderS_ msf s = arr (\a -> (s,a)) >>> runReaderS msf
src/Control/Monad/Trans/MSF/State.hs view
@@ -1,132 +1,57 @@ {-# LANGUAGE Rank2Types #-}--- | MSFs with a State monadic layer.+-- | 'MSF's with a 'State' monadic layer. ----- This module contains functions to work with MSFs that include a 'State'--- monadic layer. This includes functions to create new MSFs that include an--- additional layer, and functions to flatten that layer out of the MSF's+-- This module contains functions to work with 'MSF's that include a 'State'+-- monadic layer. This includes functions to create new 'MSF's that include an+-- additional layer, and functions to flatten that layer out of the 'MSF`'s -- transformer stack.+--+-- It is based on the _strict_ state monad 'Control.Monad.Trans.State.Strict',+-- so when combining it with other modules such as @mtl@'s,+-- the strict version has to be included, i.e. 'Control.Monad.State.Strict'+-- instead of 'Control.Monad.State' or 'Control.Monad.State.Lazy'. module Control.Monad.Trans.MSF.State ( module Control.Monad.Trans.State.Strict- -- * State MSF running/wrapping/unwrapping+ -- * 'State' 'MSF' running and wrapping , stateS , runStateS , runStateS_ , runStateS__- -- ** Alternative implementation using 'lifterS'- , stateS'- , runStateS'- -- ** Alternative implementation using 'transS'- , runStateS''- -- ** Alternative implementation using 'transG'- , runStateS''' ) where -- External import Control.Applicative import Control.Monad.Trans.State.Strict hiding (liftCallCC, liftCatch, liftListen, liftPass) -- Avoid conflicting exports+import Data.Tuple (swap) -- Internal-import Control.Monad.Trans.MSF.GenLift-import Data.MonadicStreamFunction+import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.InternalCore --- * State MSF running/wrapping/unwrapping+-- * 'State' 'MSF' running and wrapping --- | Build an MSF in the 'State' monad from one that takes the state as an+-- | Build an 'MSF' in the 'State' monad from one that takes the state as an -- extra input. This is the opposite of 'runStateS'.-stateS :: Monad m => MSF m (s, a) (s, b) -> MSF (StateT s m) a b-stateS msf = MSF $ \a -> StateT $ \s -> do- ((s', b), msf') <- unMSF msf (s, a)- return ((b, stateS msf'), s')+stateS :: (Functor m, Monad m) => MSF m (s, a) (s, b) -> MSF (StateT s m) a b+stateS = morphGS $ \f a -> StateT $ \s -> (\((s', b), c) -> ((b, c), s'))+ <$> f (s, a) --- | Build an MSF that takes a state as an extra input from one on the+-- | Build an 'MSF' that takes a state as an extra input from one on the -- 'State' monad. This is the opposite of 'stateS'.-runStateS :: Monad m => MSF (StateT s m) a b -> MSF m (s, a) (s, b)-runStateS msf = MSF $ \(s, a) -> do- ((b, msf'), s') <- runStateT (unMSF msf a) s- return ((s', b), runStateS msf')+runStateS :: (Functor m, Monad m) => MSF (StateT s m) a b -> MSF m (s, a) (s, b)+runStateS = morphGS $ \f (s, a) -> (\((b, c), s') -> ((s', b), c))+ <$> runStateT (f a) s --- | Build an MSF /function/ that takes a fixed state as additional input, from--- an MSF in the 'State' monad, and outputs the new state with every+-- | Build an 'MSF' /function/ that takes a fixed state as additional input,+-- from an 'MSF' in the 'State' monad, and outputs the new state with every -- transformation step.------ This should be always equal to:------ @--- runStateS_ msf s = feedback s $ runStateS msf >>> arr (\(s,b) -> ((s,b), s))--- @------ although possibly more efficient.---runStateS_ :: Monad m => MSF (StateT s m) a b -> s -> MSF m a (s, b)-runStateS_ msf s = MSF $ \a -> do- ((b, msf'), s') <- runStateT (unMSF msf a) s- return ((s', b), runStateS_ msf' s')---- | Build an MSF /function/ that takes a fixed state as additional--- input, from an MSF in the 'State' monad.------ This should be always equal to:------ @--- runStateS__ msf s = feedback s $ runStateS msf >>> arr (\(s,b) -> (b, s))--- @------ although possibly more efficient.---runStateS__ :: Monad m => MSF (StateT s m) a b -> s -> MSF m a b-runStateS__ msf s = MSF $ \a -> do- ((b, msf'), s') <- runStateT (unMSF msf a) s- return (b, runStateS__ msf' s')---- * Alternative implementations------ ** Alternative using running/wrapping MSF combinators using generic lifting---- ** Alternative using 'lifterS'.---- | Alternative implementation of 'stateS' using 'lifterS'.-stateS' :: (Functor m, Monad m) => MSF m (s, a) (s, b) -> MSF (StateT s m) a b-stateS' = lifterS (\g i -> StateT ((resort <$>) . g . flip (,) i))- where resort ((s, b), ct) = ((b, ct), s)---- stateS' :: Monad m => MSF m (s, a) (s, b) -> MSF (StateT s m) a b--- stateS' = lifterS $ \f a -> StateT $ \s -> do--- ((s', b), msf') <- f (s, a)--- return ((b, msf'), s')---- | Alternative implementation of 'runStateS' using 'lifterS'.-runStateS' :: (Functor m, Monad m) => MSF (StateT s m) a b -> MSF m (s, a) (s, b)-runStateS' = lifterS (\g i -> resort <$> uncurry (flip runStateT) (second g i))- where resort ((b, msf), s) = ((s, b), msf)---- ** Alternative using 'transS'.---- | Alternative implementation of 'runStateS' using 'transS'.-runStateS'' :: (Functor m, Monad m) => MSF (StateT s m) a b -> MSF m (s, a) (s, b)-runStateS'' = transS transformInput transformOutput- where- transformInput (_, a) = return a- transformOutput (s, _) msfaction = sym <$> runStateT msfaction s- sym ((b, msf), s) = ((s, b), msf)--{--stateS'' :: Monad m => MSF m (s, a) (s, b) -> MSF (StateT s m) a b-stateS'' = transS transformInput transformOutput- where- transformInput (_, a) = return a- transformOutput (s, _) = do- put s--}---- ** Alternative using 'transG'.+runStateS_ :: (Functor m, Monad m) => MSF (StateT s m) a b -> s -> MSF m a (s, b)+runStateS_ msf s = feedback s+ $ arr swap >>> runStateS msf >>> arr (\(s', b) -> ((s', b), s')) --- | Alternative implementation of 'runStateS' using 'transG'.-runStateS''' :: (Functor m, Monad m) => MSF (StateT s m) a b -> MSF m (s, a) (s, b)-runStateS''' = transG transformInput transformOutput- where- transformInput (_, a) = return a- transformOutput (s, _) msfaction = sym <$> runStateT msfaction s- sym ((b, msf), s) = ((s, b), Just msf)+-- TODO Rename this to execStateS!+-- | Build an 'MSF' /function/ that takes a fixed state as additional+-- input, from an 'MSF' in the 'State' monad.+runStateS__ :: (Functor m, Monad m) => MSF (StateT s m) a b -> s -> MSF m a b+runStateS__ msf s = runStateS_ msf s >>> arr snd
src/Control/Monad/Trans/MSF/Writer.hs view
@@ -1,99 +1,41 @@--- | MSFs with a Writer monadic layer.+-- | 'MSF's with a 'Writer' monadic layer. ----- This module contains functions to work with MSFs that include a 'Writer'--- monadic layer. This includes functions to create new MSFs that include an--- additional layer, and functions to flatten that layer out of the MSF's+-- This module contains functions to work with 'MSF's that include a 'Writer'+-- monadic layer. This includes functions to create new 'MSF's that include an+-- additional layer, and functions to flatten that layer out of the 'MSF`'s -- transformer stack.+--+-- It is based on the _strict_ writer monad 'Control.Monad.Trans.Writer.Strict',+-- so when combining it with other modules such as @mtl@'s,+-- the strict version has to be included, i.e. 'Control.Monad.Writer.Strict'+-- instead of 'Control.Monad.Writer' or 'Control.Monad.Writer.Lazy'. module Control.Monad.Trans.MSF.Writer ( module Control.Monad.Trans.Writer.Strict- -- * Writer MSF running \/ wrapping \/ unwrapping+ -- * 'Writer' 'MSF' running and wrapping , writerS , runWriterS-- -- ** Alternative implementation using 'lifterS'- , writerS'- , runWriterS'-- -- ** Alternative implementation using 'transS'- , writerS''- , runWriterS'' ) where -- External-import Control.Applicative-import Control.Monad.Trans.Class import Control.Monad.Trans.Writer.Strict hiding (liftCallCC, liftCatch, pass) -- Avoid conflicting exports+import Data.Functor ((<$>)) import Data.Monoid -- Internal-import Control.Monad.Trans.MSF.GenLift import Data.MonadicStreamFunction --- * Writer MSF running/wrapping/unwrapping+-- * 'Writer' 'MSF' running and wrapping --- | Build an MSF in the 'Writer' monad from one that produces the log as an+-- | Build an 'MSF' in the 'Writer' monad from one that produces the log as an -- extra output. This is the opposite of 'runWriterS'.-writerS :: (Monad m, Monoid s) => MSF m a (s, b) -> MSF (WriterT s m) a b-writerS msf = MSF $ \a -> do- ((s, b), msf') <- lift $ unMSF msf a- tell s- return (b, writerS msf')+writerS :: (Functor m, Monad m, Monoid w)+ => MSF m a (w, b) -> MSF (WriterT w m) a b+writerS = morphGS $ \f a -> WriterT $ (\((w, b), c) -> ((b, c), w)) <$> f a --- | Build an MSF that produces the log as an extra output from one on the+-- | Build an 'MSF' that produces the log as an extra output from one on the -- 'Writer' monad. This is the opposite of 'writerS'.-runWriterS :: Monad m => MSF (WriterT s m) a b -> MSF m a (s, b)-runWriterS msf = MSF $ \a -> do- ((b, msf'), s') <- runWriterT $ unMSF msf a- return ((s', b), runWriterS msf')---- * Alternative running/wrapping MSF combinators---- ** Alternative implementation using 'lifterS'---- | Alternative implementation of 'writerS' using 'lifterS'.-writerS' :: (Monad m, Monoid s) => MSF m a (s, b) -> MSF (WriterT s m) a b-writerS' = lifterS wrapMSFWriterT---- | Alternative implementation of 'runWriterS' using 'lifterS'.-runWriterS' :: (Monoid s, Functor m, Monad m) => MSF (WriterT s m) a b -> MSF m a (s, b)-runWriterS' = lifterS unwrapMSFWriterT---- ** Alternative implementation using 'transS'---- | Alternative implementation of 'writerS' using 'transS'.-writerS'' :: (Monad m, Monoid w) => MSF m a (w, b) -> MSF (WriterT w m) a b-writerS'' = transS transformInput transformOutput- where- transformInput = return- transformOutput _ msfaction = do- ((w, b), msf') <- lift msfaction- tell w- return (b, msf')---- | Alternative implementation of 'runWriterS' using 'transS'.-runWriterS'' :: (Monoid s, Functor m, Monad m) => MSF (WriterT s m) a b -> MSF m a (s, b)-runWriterS'' = transS transformInput transformOutput- where- transformInput = return- transformOutput _ msfaction = sym <$> runWriterT msfaction- sym ((b, msf), s) = ((s, b), msf)---- ** Wrapping/unwrapping functions------ TODO: These are *almost*-MSF-agnostic wrapping/unwrapping functions.--- The continuations (and therefore the stream functions) are still--- there, but now we know nothing about them, not even their type.--- Monadic actions carry an extra value, of some polymorphic type ct,--- which is only necessary to extract the output and the context.------ wrapMSFWriterT :: (Monad m, Functor m) => (a -> WriterT s m (b, ct)) -> a -> m ((s, b), ct)-wrapMSFWriterT :: (Monoid s, Monad m) => (a -> m ((s, b), ct)) -> a -> WriterT s m (b, ct)-wrapMSFWriterT g i = do- ((s, b), msf) <- lift $ g i- tell s- return (b, msf)--unwrapMSFWriterT :: (Monad m, Functor m) => (a -> WriterT s m (b, ct)) -> a -> m ((s, b), ct)-unwrapMSFWriterT g i = resort <$> runWriterT (g i)- where resort ((b, msf), s) = ((s, b), msf)+runWriterS :: (Functor m, Monad m)+ => MSF (WriterT s m) a b -> MSF m a (s, b)+runWriterS = morphGS $ \f a -> (\((b, c), s) -> ((s, b), c))+ <$> runWriterT (f a)
src/Data/MonadicStreamFunction.hs view
@@ -1,31 +1,32 @@ -- | Monadic Stream Functions are synchronized stream functions -- with side effects. ----- MSFs are defined by a function @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@+-- 'MSF's are defined by a function+-- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@ -- that executes one step of a simulation, and produces an output in a -- monadic context, and a continuation to be used for future steps. -- -- See the module "Data.MonadicStreamFunction.Core" for details. ----- MSFs are a generalisation of the implementation mechanism used by Yampa,+-- 'MSF's are a generalisation of the implementation mechanism used by Yampa, -- Wormholes and other FRP and reactive implementations. -- -- When combined with different monads, they produce interesting effects. For--- example, when combined with the @Maybe@ monad, they become transformations--- that may stop producing outputs (and continuations). The @Either@ monad--- gives rise to MSFs that end with a result (akin to Tasks in Yampa, and+-- example, when combined with the 'Maybe' monad, they become transformations+-- that may stop producing outputs (and continuations). The 'Either' monad+-- gives rise to 'MSF's that end with a result (akin to Tasks in Yampa, and -- Monadic FRP). -- -- Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a b@ -- for a specific transformer @t@ often gives rise to known FRP constructs.--- For instance, flattening with @EitherT@ gives rise to switching, and--- flattening with @ListT@ gives rise to parallelism with broadcasting.+-- For instance, flattening with 'EitherT' gives rise to switching, and+-- flattening with 'ListT' gives rise to parallelism with broadcasting. ----- MSFs can be used to implement many FRP variants, including Arrowized FRP,+-- 'MSF's can be used to implement many FRP variants, including Arrowized FRP, -- Classic FRP, and plain reactive programming. Arrowized and applicative -- syntax are both supported. ----- For a very detailed introduction to MSFs, see:+-- For a very detailed introduction to 'MSF's, see: -- <http://dl.acm.org/citation.cfm?id=2976010> -- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>). --
src/Data/MonadicStreamFunction/Async.hs view
@@ -5,6 +5,7 @@ -- Internal import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.InternalCore import Data.MonadicStreamFunction.Util (MStream) {- |@@ -13,7 +14,7 @@ Example: ->>> let intstream = arrM_ $ putStrLn "Enter a list of Ints:" >> readLn :: MStream IO [Int]+>>> let intstream = constS $ putStrLn "Enter a list of Ints:" >> readLn :: MStream IO [Int] >>> reactimate $ concatS intstream >>> arrM print Enter a list of Ints: [1,2,33]
src/Data/MonadicStreamFunction/Core.hs view
@@ -1,229 +1,189 @@-{-# LANGUAGE ExplicitForAll #-}-{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE Rank2Types #-} -- | Monadic Stream Functions are synchronized stream functions -- with side effects. ----- MSFs are defined by a function @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@+-- 'MSF's are defined by a function+-- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@ -- that executes one step of a simulation, and produces an output in a -- monadic context, and a continuation to be used for future steps. ----- MSFs are a generalisation of the implementation mechanism used by Yampa,+-- 'MSF's are a generalisation of the implementation mechanism used by Yampa, -- Wormholes and other FRP and reactive implementations. -- -- When combined with different monads, they produce interesting effects. For--- example, when combined with the @Maybe@ monad, they become transformations--- that may stop producing outputs (and continuations). The @Either@ monad--- gives rise to MSFs that end with a result (akin to Tasks in Yampa, and+-- example, when combined with the 'Maybe' monad, they become transformations+-- that may stop producing outputs (and continuations). The 'Either' monad+-- gives rise to 'MSF's that end with a result (akin to Tasks in Yampa, and -- Monadic FRP). -- -- Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a b@ -- for a specific transformer @t@ often gives rise to known FRP constructs.--- For instance, flattening with @EitherT@ gives rise to switching, and--- flattening with @ListT@ gives rise to parallelism with broadcasting.+-- For instance, flattening with 'EitherT' gives rise to switching, and+-- flattening with 'ListT' gives rise to parallelism with broadcasting. ----- MSFs can be used to implement many FRP variants, including Arrowized FRP,+-- 'MSF's can be used to implement many FRP variants, including Arrowized FRP, -- Classic FRP, and plain reactive programming. Arrowized and applicative -- syntax are both supported. ----- For a very detailed introduction to MSFs, see:+-- For a very detailed introduction to 'MSF's, see: -- <http://dl.acm.org/citation.cfm?id=2976010> -- (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).---- NOTE TO IMPLEMENTORS:------ This module contains the core. Only the core. It should be possible--- to define every function and type outside this module, except for the--- instances for ArrowLoop, ArrowChoice, etc., without access to the--- internal constructor for MSF and the function 'unMSF'.------ It's very hard to know what IS essential to framework and if we start--- adding all the functions and instances that *may* be useful in one--- module.------ By separating some instances and functions in other modules , we can--- easily understand what is the essential idea and then analyse how it--- is affected by an extension. It also helps demonstrate that something--- works for MSFs + ArrowChoice, or MSFs + ArrowLoop, etc.------ To address potential violations of basic design principles (like 'not--- having orphan instances'), the main module Data.MonadicStreamFunction--- exports everything. Users should *never* import this module here--- individually, but the main module instead.-module Data.MonadicStreamFunction.Core where+module Data.MonadicStreamFunction.Core+ ( -- * Types+ MSF+ -- * Lifting and Monadic transformations+ -- ** Lifting point-wise computations+ , constM+ , arrM+ , liftBaseM+ -- ** Trans-monadic MSF combinators+ -- *** MonadBase+ , liftBaseS+ , (^>>>)+ , (>>>^)+ -- *** MonadTrans+ , liftTransS+ -- *** Generic Monadic Transformations+ , morphS+ , morphGS+ -- * Depending on the past+ , feedback+ -- * Simulation+ , reactimate+ , embed+ , module Control.Arrow+ )+ where --- External-import Control.Arrow import Control.Applicative-import Control.Category (Category(..))-import Control.Monad+import Control.Arrow+import Control.Category as C import Control.Monad.Base import Control.Monad.Trans.Class+import Data.Tuple (swap) import Prelude hiding ((.), id, sum) +import Data.MonadicStreamFunction.InternalCore (MSF, morphGS, feedback, reactimate, embed)+ -- * Definitions --- | Stepwise, side-effectful MSFs without implicit knowledge of time.------ MSFs should be applied to streams or executed indefinitely or until they--- terminate. See 'reactimate' and 'reactimateB' for details. In general,--- calling the value constructor 'MSF' or the function 'unMSF' is discouraged.-data MSF m a b = MSF { unMSF :: a -> m (b, MSF m a b) }+-- | 'Arrow' instance for 'MSF's.+instance Monad m => Arrow (MSF m) where --- Instances+ arr f = arrM (return . f) --- | Instance definition for 'Category'. Defines 'id' and '.'.-instance Monad m => Category (MSF m) where- id = go- where go = MSF $ \a -> return (a, go)- sf2 . sf1 = MSF $ \a -> do- (b, sf1') <- unMSF sf1 a- (c, sf2') <- unMSF sf2 b- let sf' = sf2' . sf1'- c `seq` return (c, sf')+ -- first sf = MSF $ \(a,c) -> do+ -- (b, sf') <- unMSF sf a+ -- b `seq` return ((b, c), first sf') --- | 'Arrow' instance for MSFs.-instance Monad m => Arrow (MSF m) where+ first = morphGS $ \f (a,c) -> do+ (b, msf') <- f a+ return ((b, c), msf') - arr f = go- where go = MSF $ \a -> return (f a, go) - first sf = MSF $ \(a,c) -> do- (b, sf') <- unMSF sf a- b `seq` return ((b, c), first sf')+-- * Functor and applicative instances --- | Functor instance for MSFs.-instance Functor m => Functor (MSF m a) where- -- fmap f msf == msf >>> arr f- fmap f msf = MSF $ fmap fS . unMSF msf- where- fS (b, cont) = (f b, fmap f cont)+-- | 'Functor' instance for 'MSF's.+instance Monad m => Functor (MSF m a) where+ fmap f msf = msf >>> arr f+ -- fmap f msf = MSF $ fmap fS . unMSF msf+ -- where+ -- fS (b, cont) = (f b, fmap f cont) --- | Applicative instance for MSFs.+-- | 'Applicative' instance for 'MSF's. instance (Functor m, Monad m) => Applicative (MSF m a) where -- It is possible to define this instance with only Applicative m pure = arr . const fs <*> bs = (fs &&& bs) >>> arr (uncurry ($)) --- * Monadic computations and MSFs -- ** Lifting point-wise computations +-- | Lifts a monadic computation into a Stream.+constM :: Monad m => m b -> MSF m a b+constM = arrM . const+ -- | Apply a monadic transformation to every element of the input stream. -- -- Generalisation of 'arr' from 'Arrow' to monadic functions. arrM :: Monad m => (a -> m b) -> MSF m a b-arrM f = go- where go = MSF $ \a -> do- b <- f a- return (b, go)+--arrM f = go+-- where go = MSF $ \a -> do+-- b <- f a+-- return (b, go)+arrM f = morphGS (\i a -> i a >>= \(_,c) -> f a >>= \b -> return (b, c)) C.id -- | Monadic lifting from one monad into another-liftS :: (Monad m2, MonadBase m1 m2) => (a -> m1 b) -> MSF m2 a b-liftS = arrM . (liftBase .)+liftBaseM :: (Monad m2, MonadBase m1 m2) => (a -> m1 b) -> MSF m2 a b+liftBaseM = arrM . (liftBase .) --- ** Lifting MSFs+-- ** MSF combinators that apply monad transformations --- *** Lifting across monad stacks+-- | Lift innermost monadic actions in monad stack (generalisation of+-- 'liftIO').+liftBaseS :: (Monad m2, MonadBase m1 m2) => MSF m1 a b -> MSF m2 a b+liftBaseS = morphS liftBase +-- *** MonadBase+-- | Lift the first 'MSF' into the monad of the second.+(^>>>) :: MonadBase m1 m2 => MSF m1 a b -> MSF m2 b c -> MSF m2 a c+sf1 ^>>> sf2 = liftBaseS sf1 >>> sf2+{-# INLINE (^>>>) #-}++-- | Lift the second 'MSF' into the monad of the first.+(>>>^) :: MonadBase m1 m2 => MSF m2 a b -> MSF m1 b c -> MSF m2 a c+sf1 >>>^ sf2 = sf1 >>> liftBaseS sf2+{-# INLINE (>>>^) #-}++-- *** MonadTrans+ -- | Lift inner monadic actions in monad stacks. -liftMSFTrans :: (MonadTrans t, Monad m, Monad (t m))- => MSF m a b- -> MSF (t m) a b-liftMSFTrans = liftMSFPurer lift+liftTransS :: (MonadTrans t, Monad m, Monad (t m))+ => MSF m a b+ -> MSF (t m) a b+liftTransS = morphS lift --- | Lift innermost monadic actions in a monad stacks (generalisation of--- 'liftIO').-liftMSFBase :: (Monad m2, MonadBase m1 m2) => MSF m1 a b -> MSF m2 a b-liftMSFBase = liftMSFPurer liftBase+-- *** Generic monadic transformation --- *** Generic MSF Lifting+-- | Apply trans-monadic actions (in an arbitrary way).+--+-- This is just a convenience function when you have a function to move across+-- monads, because the signature of 'morphGS' is a bit complex.+morphS :: (Monad m2, Monad m1)+ => (forall c . m1 c -> m2 c)+ -> MSF m1 a b+ -> MSF m2 a b+morphS morph = morphGS morph'+ where+ -- The following makes the a's and the b's the same, and it just says:+ -- whatever function m1F you give me to apply to every sample, I use morph+ -- on the result to go from m1 to m2.+ --+ -- Remember that:+ -- morphGS :: Monad m2+ -- => (forall c . (a1 -> m1 (b1, c)) -> (a2 -> m2 (b2, c)))+ -- -- ^ The natural transformation. @mi@, @ai@ and @bi@ for @i = 1, 2@+ -- -- can be chosen freely, but @c@ must be universally quantified+ -- -> MSF m1 a1 b1+ -- -> MSF m2 a2 b2+ --+ -- morph' :: (forall c . (a -> m1 (b, c)) -> (a -> m2 (b, c)))+ morph' m1F = morph . m1F -- IPerez: There is an alternative signature for liftMStreamPurer that also -- works, and makes the code simpler: ----- liftMSFPurer :: Monad m => (m1 (b, MSF m1 a b) -> m (b, MSF m1 a b)) -> MSF m1 a b -> MSF m a b+-- morphS :: Monad m => (m1 (b, MSF m1 a b) -> m (b, MSF m1 a b)) -> MSF m1 a b -> MSF m a b -- -- Then we can express: ----- liftMSFTrans = liftMSFPurer lift--- liftMSFBase = liftMSFPurer liftBase+-- liftTransS = morphS lift+-- liftBaseS = morphS liftBase ----- We could also define a strict version of liftMSFPurer as follows:+-- We could also define a strict version of morphS as follows: ----- liftMStreamPurer' f = liftMSFPurer (f >=> whnfVal)+-- morphS' f = morphS (f >=> whnfVal) -- where whnfVal p@(b,_) = b `seq` return p ----- and leave liftMSFPurer as a lazy version (by default).---- | Lifting purer monadic actions (in an arbitrary way)-liftMSFPurer :: (Monad m2, Monad m1) => (forall c . m1 c -> m2 c) -> MSF m1 a b -> MSF m2 a b-liftMSFPurer liftPurer sf = MSF $ \a -> do- (b, sf') <- liftPurer $ unMSF sf a- b `seq` return (b, liftMSFPurer liftPurer sf')---- * Delays---- | Delay a signal by one sample.-iPre :: Monad m- => a -- ^ First output- -> MSF m a a-iPre firsta = MSF $ \a -> return (firsta, delay a)--- iPre firsta = feedback firsta $ lift swap--- where swap (a,b) = (b, a)--- iPre firsta = next firsta identity---- | See 'iPre'.---- FIXME: Remove delay from this module. We should try to make this module--- small, keeping only primitives.-delay :: Monad m => a -> MSF m a a-delay = iPre---- * Switching---- | Switching applies one MSF until it produces a 'Just' output, and then--- "turns on" a continuation and runs it.------ A more advanced and comfortable approach to switching is given by Exceptions--- in 'Control.Monad.Trans.MSF.Except'-switch :: Monad m => MSF m a (b, Maybe c) -> (c -> MSF m a b) -> MSF m a b-switch sf f = MSF $ \a -> do- ((b, c), sf') <- unMSF sf a- return (b, maybe (switch sf' f) f c)---- * Feedback loops---- | Well-formed looped connection of an output component as a future input.-feedback :: Monad m => c -> MSF m (a, c) (b, c) -> MSF m a b-feedback c sf = MSF $ \a -> do- ((b', c'), sf') <- unMSF sf (a, c)- return (b', feedback c' sf')---- * Execution/simulation---- | Apply a monadic stream function to a list.------ Because the result is in a monad, it may be necessary to--- traverse the whole list to evaluate the value in the results to WHNF.--- For example, if the monad is the maybe monad, this may not produce anything--- if the MSF produces Nothing at any point, so the output stream cannot--- consumed progressively.------ To explore the output progressively, use 'liftMSF' and '(>>>)'', together--- with some action that consumes/actuates on the output.------ This is called 'runSF' in Liu, Cheng, Hudak, "Causal Commutative Arrows and--- Their Optimization"-embed :: Monad m => MSF m a b -> [a] -> m [b]-embed _ [] = return []-embed sf (a:as) = do- (b, sf') <- unMSF sf a- bs <- embed sf' as- return (b:bs)---- | Run an 'MSF' indefinitely passing a unit-carrying input stream.-reactimate :: Monad m => MSF m () () -> m ()-reactimate sf = do- (_, sf') <- unMSF sf ()- reactimate sf'+-- and leave morphS as a lazy version (by default).
src/Data/MonadicStreamFunction/Instances/ArrowChoice.hs view
@@ -8,6 +8,7 @@ import Control.Arrow import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.InternalCore -- | 'ArrowChoice' instance for MSFs. instance Monad m => ArrowChoice (MSF m) where
src/Data/MonadicStreamFunction/Instances/ArrowLoop.hs view
@@ -9,6 +9,7 @@ module Data.MonadicStreamFunction.Instances.ArrowLoop where import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.InternalCore -- External import Control.Arrow@@ -16,7 +17,7 @@ -- | 'ArrowLoop' instance for MSFs. The monad must be an instance of -- 'MonadFix'.-instance (Monad m, MonadFix m) => ArrowLoop (MSF m) where+instance MonadFix m => ArrowLoop (MSF m) where loop :: MSF m (b, d) (c, d) -> MSF m b c loop sf = MSF $ \a -> do rec ((b,c), sf') <- unMSF sf (a, c)
src/Data/MonadicStreamFunction/Instances/ArrowPlus.hs view
@@ -6,10 +6,14 @@ -- This is only defined for monads that are instances of 'MonadPlus'. module Data.MonadicStreamFunction.Instances.ArrowPlus where +-- base import Control.Arrow import Control.Monad+import Control.Applicative +-- dunai import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.InternalCore -- | Instance of 'ArrowZero' for Monadic Stream Functions ('MSF'). -- The monad must be an instance of 'MonadPlus'.@@ -20,3 +24,7 @@ -- The monad must be an instance of 'MonadPlus'. instance (Monad m, MonadPlus m) => ArrowPlus (MSF m) where sf1 <+> sf2 = MSF $ \a -> unMSF sf1 a `mplus` unMSF sf2 a++instance (Functor m, Monad m, MonadPlus m) => Alternative (MSF m a) where+ empty = zeroArrow+ (<|>) = (<+>)
src/Data/MonadicStreamFunction/Instances/Num.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-} --- | Number instances for MSFs that produce numbers. This allows you to use--- numeric operators with MSFs that output numbers, for example, you can write:+-- | Number instances for 'MSF's that produce numbers. This allows you to use+-- numeric operators with 'MSF's that output numbers, for example,+-- you can write: -- -- @ -- msf1 :: MSF Input Double -- defined however you want@@ -25,7 +26,7 @@ import Control.Arrow.Util import Data.MonadicStreamFunction.Core --- | 'Num' instance for MSFs.+-- | 'Num' instance for 'MSF's. instance (Monad m, Num b) => Num (MSF m a b) where (+) = elementwise2 (+) (-) = elementwise2 (-)@@ -35,13 +36,13 @@ negate = elementwise negate fromInteger = constantly . fromInteger --- | 'Fractional' instance for MSFs.+-- | 'Fractional' instance for 'MSF's. instance (Monad m, Fractional b) => Fractional (MSF m a b) where fromRational = constantly . fromRational (/) = elementwise2 (/) recip = elementwise recip --- | 'Floating' instance for MSFs.+-- | 'Floating' instance for 'MSF's. instance (Monad m, Floating b) => Floating (MSF m a b) where pi = constantly pi exp = elementwise exp
src/Data/MonadicStreamFunction/Instances/VectorSpace.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-}--- | 'VectorSpace' instances for MSFs that produce vector spaces. This allows--- you to use vector operators with MSFs that output vectors, for example, you+-- | 'VectorSpace' instances for 'MSF's that produce vector spaces. This allows+-- you to use vector operators with 'MSF's that output vectors, for example, you -- can write: -- -- @@@ -28,7 +28,7 @@ -- These conflict with Data.VectorSpace.Instances --- | R-module instance for MSFs.+-- | R-module instance for 'MSF's. instance (Monad m, RModule v) => RModule (MSF m a v) where type Groundring (MSF m a v) = Groundring v zeroVector = constantly zeroVector@@ -37,6 +37,6 @@ (^+^) = elementwise2 (^+^) (^-^) = elementwise2 (^-^) --- | Vector-space instance for MSFs.+-- | Vector-space instance for 'MSF's. instance (Monad m, VectorSpace v) => VectorSpace (MSF m a v) where msf ^/ r = msf >>^ (^/ r)
+ src/Data/MonadicStreamFunction/InternalCore.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE ExplicitForAll #-}+{-# LANGUAGE Rank2Types #-}+-- | Monadic Stream Functions are synchronized stream functions+-- with side effects.+--+-- 'MSF's are defined by a function+-- @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@+-- that executes one step of a simulation, and produces an output in a+-- monadic context, and a continuation to be used for future steps.+--+-- 'MSF's are a generalisation of the implementation mechanism used by Yampa,+-- Wormholes and other FRP and reactive implementations.+--+-- This modules defines only the minimal core. Hopefully, other functions can be+-- defined in terms of the functions in this module without accessing the+-- MSF constuctor.+++-- NOTE TO IMPLEMENTORS:+--+-- This module contains the core. Only the core. It should be possible+-- to define every function and type outside this module, except for the+-- instances for ArrowLoop, ArrowChoice, etc., without access to the+-- internal constructor for MSF and the function 'unMSF'.+--+-- It's very hard to know what IS essential to framework and if we start+-- adding all the functions and instances that *may* be useful in one+-- module.+--+-- By separating some instances and functions in other modules, we can+-- easily understand what is the essential idea and then analyse how it+-- is affected by an extension. It also helps demonstrate that something+-- works for MSFs + ArrowChoice, or MSFs + ArrowLoop, etc.+--+-- To address potential violations of basic design principles (like 'not+-- having orphan instances'), the main module Data.MonadicStreamFunction+-- exports everything. Users should *never* import this module here+-- individually, but the main module instead.++module Data.MonadicStreamFunction.InternalCore where++-- External+import Control.Arrow+import Control.Applicative+import Control.Category (Category(..))+import Control.Monad+import Control.Monad.Base+import Control.Monad.Trans.Class+import Prelude hiding ((.), id, sum)++-- * Definitions++-- | Stepwise, side-effectful 'MSF's without implicit knowledge of time.+--+-- 'MSF's should be applied to streams or executed indefinitely or until they+-- terminate. See 'reactimate' and 'reactimateB' for details. In general,+-- calling the value constructor 'MSF' or the function 'unMSF' is discouraged.+data MSF m a b = MSF { unMSF :: a -> m (b, MSF m a b) }++-- Instances++-- | Instance definition for 'Category'. Defines 'id' and '.'.+instance Monad m => Category (MSF m) where+ id = go+ where go = MSF $ \a -> return (a, go)+ sf2 . sf1 = MSF $ \a -> do+ (b, sf1') <- unMSF sf1 a+ (c, sf2') <- unMSF sf2 b+ let sf' = sf2' . sf1'+ c `seq` return (c, sf')++-- * Monadic computations and 'MSF's++-- | Generic lifting of a morphism to the level of 'MSF's.+--+-- Natural transformation to the level of 'MSF's.+--+-- __Mathematical background:__ The type @a -> m (b, c)@ is a functor in @c@,+-- and @MSF m a b@ is its greatest fixpoint, i.e. it is isomorphic to the type+-- @a -> m (b, MSF m a b)@, by definition.+-- The types @m@, @a@ and @b@ are parameters of the functor.+-- Taking a fixpoint is functorial itself, meaning that a morphism+-- (a natural transformation) of two such functors gives a morphism+-- (an ordinary function) of their fixpoints.+--+-- This is in a sense the most general "abstract" lifting function,+-- i.e. the most general one that only changes input, output and side effect+-- types, and doesn't influence control flow.+-- Other handling functions like exception handling or 'ListT' broadcasting+-- necessarily change control flow.+morphGS :: Monad m2+ => (forall c . (a1 -> m1 (b1, c)) -> (a2 -> m2 (b2, c)))+ -- ^ The natural transformation. @mi@, @ai@ and @bi@ for @i = 1, 2@+ -- can be chosen freely, but @c@ must be universally quantified+ -> MSF m1 a1 b1+ -> MSF m2 a2 b2+morphGS morph msf = MSF $ \a2 -> do+ (b2, msf') <- morph (unMSF msf) a2+ return (b2, morphGS morph msf')++-- * Feedback loops++-- | Well-formed looped connection of an output component as a future input.+feedback :: Monad m => c -> MSF m (a, c) (b, c) -> MSF m a b+feedback c sf = MSF $ \a -> do+ ((b', c'), sf') <- unMSF sf (a, c)+ return (b', feedback c' sf')++-- * Execution/simulation++-- | Apply a monadic stream function to a list.+--+-- Because the result is in a monad, it may be necessary to+-- traverse the whole list to evaluate the value in the results to WHNF.+-- For example, if the monad is the maybe monad, this may not produce anything+-- if the 'MSF' produces 'Nothing' at any point, so the output stream cannot+-- consumed progressively.+--+-- To explore the output progressively, use 'liftMSF' and '(>>>)'', together+-- with some action that consumes/actuates on the output.+--+-- This is called 'runSF' in Liu, Cheng, Hudak, "Causal Commutative Arrows and+-- Their Optimization"+embed :: Monad m => MSF m a b -> [a] -> m [b]+embed _ [] = return []+embed sf (a:as) = do+ (b, sf') <- unMSF sf a+ bs <- embed sf' as+ return (b:bs)++-- | Run an 'MSF' indefinitely passing a unit-carrying input stream.+reactimate :: Monad m => MSF m () () -> m ()+reactimate sf = do+ (_, sf') <- unMSF sf ()+ reactimate sf'
src/Data/MonadicStreamFunction/Parallel.hs view
@@ -8,9 +8,10 @@ -- Internal import Data.MonadicStreamFunction+import Data.MonadicStreamFunction.InternalCore --- | Run two MSFs in parallel, taking advantage of parallelism if--- possible. This is the parallel version of '(***)'.+-- | Run two 'MSF's in parallel, taking advantage of parallelism if+-- possible. This is the parallel version of '***'. (*|*) :: Monad m => MSF m a b -> MSF m c d -> MSF m (a, c) (b, d) msf1 *|* msf2 = MSF $ \(a, c) -> do@@ -18,6 +19,6 @@ (d, msf2') <- unMSF msf2 c b `par` d `pseq` return ((b, d), msf1' *|* msf2') --- | Parallel version of '(&&&)'.+-- | Parallel version of '&&&'. (&|&) :: Monad m => MSF m a b -> MSF m a c -> MSF m a (b, c) msf1 &|& msf2 = arr (\a -> (a, a)) >>> (msf1 *|* msf2)
src/Data/MonadicStreamFunction/ReactHandle.hs view
@@ -1,5 +1,5 @@--- | ReactHandle-+-- | 'ReactHandle's.+-- -- Sometimes it is beneficial to give control to an external main loop, -- for example OpenGL or a hardware-clocked audio server like JACK. -- This module makes Dunai compatible with external main loops.@@ -12,34 +12,24 @@ -- Internal import Data.MonadicStreamFunction+import Data.MonadicStreamFunction.InternalCore --- | A storage for the current state of an MSF+-- | A storage for the current state of an 'MSF'.+-- The 'MSF' may not require input or produce output data,+-- all such data must be handled through side effects+-- (such as wormholes). type ReactHandle m = IORef (MSF m () ()) --- | Needs to be called before the external main loop is dispatched+-- | Needs to be called before the external main loop is dispatched. reactInit :: MonadIO m => MSF m () () -> m (ReactHandle m) reactInit = liftIO . newIORef --- | The callback that needs to be called by the main loop at every cycle+-- | The callback that needs to be called by the external loop at every cycle. react :: MonadIO m => ReactHandle m -> m () react handle = do msf <- liftIO $ readIORef handle (_, msf') <- unMSF msf () liftIO $ writeIORef handle msf'----- | Creates two ends of a synchronisation wormhole---- Often, the external framework may have several parallel loops,--- for example, OpenGL with a display callback, an idle callback and a keyboard callback.--- In such cases, one would like to let the different parts communicate.--- This is done through a wormhole, which is a shared mutable variable--- that can be written from one part and read from the other.--createWormhole :: MonadIO m => a -> m (MSF m a (), MSF m () a)-createWormhole a = liftIO $ do- ref <- newIORef a- return (arrM $ liftIO . writeIORef ref, arrM_ $ liftIO $ readIORef ref)
src/Data/MonadicStreamFunction/Util.hs view
@@ -1,66 +1,41 @@-{-# LANGUAGE Arrows #-}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE Rank2Types #-} -- | Useful auxiliary functions and definitions. module Data.MonadicStreamFunction.Util where -- External-import Control.Applicative import Control.Arrow import Control.Category import Control.Monad import Control.Monad.Base import Data.Monoid-import Prelude hiding (id, (.)) -- Internal import Data.MonadicStreamFunction.Core-import Data.MonadicStreamFunction.Instances.ArrowChoice+import Data.MonadicStreamFunction.Instances.ArrowChoice () import Data.VectorSpace+import Prelude hiding (id, (.)) +import Control.Monad.Trans.MSF.State+ -- * Streams and sinks --- | A stream is an MSF that produces outputs ignoring the input. It can--- obtain the values from a monadic context.+-- | A stream is an 'MSF' that produces outputs, while ignoring the input.+-- It can obtain the values from a monadic context. type MStream m a = MSF m () a --- | A stream is an MSF that produces outputs producing no output. It can--- consume the values with side effects.+-- | A sink is an 'MSF' that consumes inputs, while producing no output.+-- It can consume the values with side effects. type MSink m a = MSF m a () -- * Lifting --- | Pre-inserts an input sample.-{-# DEPRECATED insert "Don't use this. arrM id instead" #-}-insert :: Monad m => MSF m (m a) a-insert = arrM id --- | Lifts a computation into a Stream.-arrM_ :: Monad m => m b -> MSF m a b-arrM_ = arrM . const --- | Lift the first MSF into the monad of the second.-(^>>>) :: MonadBase m1 m2 => MSF m1 a b -> MSF m2 b c -> MSF m2 a c-sf1 ^>>> sf2 = liftMSFBase sf1 >>> sf2-{-# INLINE (^>>>) #-}+-- * Analogues of 'map' and 'fmap' --- | Lift the second MSF into the monad of the first.-(>>>^) :: MonadBase m1 m2 => MSF m2 a b -> MSF m1 b c -> MSF m2 a c-sf1 >>>^ sf2 = sf1 >>> liftMSFBase sf2-{-# INLINE (>>>^) #-} --- * Analogues of map and fmap---- | Apply an MSF to every input.-mapMSF :: Monad m => MSF m a b -> MSF m [a] [b]-mapMSF = MSF . consume- where- consume :: Monad m => MSF m a t -> [a] -> m ([t], MSF m [a] [t])- consume sf [] = return ([], mapMSF sf)- consume sf (a:as) = do- (b, sf') <- unMSF sf a- (bs, sf'') <- consume sf' as- b `seq` return (b:bs, sf'')---- | Apply an MSF to every input. Freezes temporarily if the input is+-- | Apply an 'MSF' to every input. Freezes temporarily if the input is -- 'Nothing', and continues as soon as a 'Just' is received. mapMaybeS :: Monad m => MSF m a b -> MSF m (Maybe a) (Maybe b) mapMaybeS msf = proc maybeA -> case maybeA of@@ -82,14 +57,26 @@ -- See also: 'iPre' --- | Preprends a fixed output to an MSF. The first input is completely+-- | Delay a signal by one sample.+iPre :: Monad m+ => a -- ^ First output+ -> MSF m a a+-- iPre firsta = MSF $ \a -> return (firsta, iPre a)+iPre firsta = feedback firsta $ arr swap+ where swap (a,b) = (b, a)+-- iPre firsta = next firsta identity+++-- | Preprends a fixed output to an 'MSF'. The first input is completely -- ignored. iPost :: Monad m => b -> MSF m a b -> MSF m a b-iPost b sf = MSF $ \_ -> return (b, sf)+iPost b sf = sf >>> (feedback (Just b) $ arr $ \(c, ac) -> case ac of+ Nothing -> (c, Nothing)+ Just b' -> (b', Nothing)) --- | Preprends a fixed output to an MSF, shifting the output.+-- | Preprends a fixed output to an 'MSF', shifting the output. next :: Monad m => b -> MSF m a b -> MSF m a b-next b sf = sf >>> delay b+next b sf = sf >>> iPre b -- | Buffers and returns the elements in FIFO order, -- returning 'Nothing' whenever the buffer is empty.@@ -102,7 +89,7 @@ -- * Folding --- ** Folding for VectorSpace instances+-- ** Folding for 'VectorSpace' instances -- | Count the number of simulation steps. Produces 1, 2, 3,... count :: (Num n, Monad m) => MSF m a n@@ -129,12 +116,18 @@ -- ** Generic folding \/ accumulation --- | Applies a function to the input and an accumulator, outputing the--- accumulator. Equal to @\f s0 -> feedback s0 $ arr (uncurry f >>> dup)@.+-- | Applies a function to the input and an accumulator,+-- outputting the updated accumulator.+-- Equal to @\f s0 -> feedback s0 $ arr (uncurry f >>> dup)@. accumulateWith :: Monad m => (a -> s -> s) -> s -> MSF m a s accumulateWith f s0 = feedback s0 $ arr g where g (a, s) = let s' = f a s in (s', s')++-- | Applies a transfer function to the input and an accumulator,+-- returning the updated accumulator and output.+mealy :: Monad m => (a -> s -> (b, s)) -> s -> MSF m a b+mealy f s0 = feedback s0 $ arr $ uncurry f -- * Unfolding
src/Data/VectorSpace.hs view
@@ -1,6 +1,7 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- | -- Module : Data.VectorSpace -- Copyright : (c) Ivan Perez and Manuel Bärenz@@ -16,7 +17,7 @@ module Data.VectorSpace where --------------------------------------------------------------------------------- Vector space type relation+-- * Vector space classes ------------------------------------------------------------------------------ infixr 6 *^@@ -77,15 +78,13 @@ (^/) :: v -> Groundfield v -> v v ^/ a = (1/a) *^ v --- TODO Why is this not a type synonym? -- | The ground ring of a vector space is required to be commutative -- and to possess inverses. -- It is then called the "ground field". -- Commutativity amounts to the law @a * b = b * a@, -- and the existence of inverses is given -- by the requirement of the 'Fractional' type class.-type family Groundfield v :: *-type instance Groundfield v = Groundring v+type Groundfield v = Groundring v -- | An inner product space is a module with an inner product, -- i.e. a map @dot@ satisfying@@ -105,15 +104,220 @@ -- -- A typical example is @sqrt (v `dot` v)@, -- for an inner product space.-class RModule v => NormedSpace v where+class (Floating (Groundfield v), InnerProductSpace v, VectorSpace v) => NormedSpace v where norm :: v -> Groundfield v+ norm v = sqrt $ v `dot` v -{--instance (Floating (Groundfield v), VectorSpace v, InnerProductSpace v) => NormedSpace v where- norm v = sqrt (v `dot` v)--}-{- I'd like to know why this won't work-normalize :: (Eq a, NormedSpace v a) => v -> v+-- | Divides a vector by its norm, resulting in a vector of norm 1.+-- Throws an error on vectors with norm 0.+normalize :: (Eq (Groundfield v), NormedSpace v) => v -> v normalize v = if nv /= 0 then v ^/ nv else error "normalize: zero vector"- where nv = norm v- -}+ where nv = norm v+++-----------------------------+-- Instances for scalar types+-----------------------------+++instance RModule Int where+ type Groundring Int = Int+ (^+^) = (+)+ (^*) = (*)+ zeroVector = 0++instance RModule Integer where+ type Groundring Integer = Integer+ (^+^) = (+)+ (^*) = (*)+ zeroVector = 0++instance RModule Double where+ type Groundring Double = Double+ (^+^) = (+)+ (^*) = (*)+ zeroVector = 0++instance RModule Float where+ type Groundring Float = Float+ (^+^) = (+)+ (^*) = (*)+ zeroVector = 0++instance VectorSpace Double where++instance VectorSpace Float where++-----------------------+-- Instances for tuples+-----------------------+++instance+ ( Groundring a ~ Groundring b+ , RModule a, RModule b+ ) => RModule (a, b) where+ type Groundring (a, b) = Groundring a+ zeroVector = (zeroVector, zeroVector)+ (a, b) ^* x = (a ^* x, b ^* x)+ (a1, b1) ^+^ (a2, b2) = (a1 ^+^ a2, b1 ^+^ b2)++instance+ (Groundfield a ~ Groundfield b+ , VectorSpace a, VectorSpace b+ ) => VectorSpace (a, b) where+ (a, b) ^/ x = (a ^/ x, b ^/ x)++instance (Groundfield a ~ Groundfield b, InnerProductSpace a, InnerProductSpace b) => InnerProductSpace (a, b) where+ (a1, b1) `dot` (a2, b2) = (a1 `dot` a2) + (b1 `dot` b2)++instance (Groundfield a ~ Groundfield b, NormedSpace a, NormedSpace b) => NormedSpace (a, b) where++-- ** Utilities to work with n-tuples for n = 3, 4, 5++break3Tuple :: (a, b, c) -> ((a, b), c)+break3Tuple (a, b, c) = ((a, b), c)++join3Tuple :: ((a, b), c) -> (a, b, c)+join3Tuple ((a, b), c) = (a, b, c)++break4Tuple :: (a, b, c, d) -> ((a, b), (c, d))+break4Tuple (a, b, c, d) = ((a, b), (c, d))++join4Tuple :: ((a, b), (c, d)) -> (a, b, c, d)+join4Tuple ((a, b), (c, d)) = (a, b, c, d)++break5Tuple :: (a, b, c, d, e) -> ((a, b), (c, d, e))+break5Tuple (a, b, c, d, e) = ((a, b), (c, d, e))++join5Tuple :: ((a, b), (c, d, e)) -> (a, b, c, d, e)+join5Tuple ((a, b), (c, d, e)) = (a, b, c, d, e)++++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , RModule a, RModule b, RModule c+ ) => RModule (a, b, c) where+ type Groundring (a, b, c) = Groundring a+ zeroVector = join3Tuple zeroVector+ a *^ v = join3Tuple $ a *^ (break3Tuple v)+ v1 ^+^ v2 = join3Tuple $ break3Tuple v1 ^+^ break3Tuple v2++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , VectorSpace a, VectorSpace b, VectorSpace c+ ) => VectorSpace (a, b, c) where++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , InnerProductSpace a, InnerProductSpace b, InnerProductSpace c+ ) => InnerProductSpace (a, b, c) where+ v1 `dot` v2 = break3Tuple v1 `dot` break3Tuple v2++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , NormedSpace a, NormedSpace b, NormedSpace c+ ) => NormedSpace (a, b, c) where++++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , RModule a, RModule b, RModule c, RModule d+ ) => RModule (a, b, c, d) where+ type Groundring (a, b, c, d) = Groundring a+ zeroVector = join4Tuple zeroVector+ a *^ v = join4Tuple $ a *^ (break4Tuple v)+ v1 ^+^ v2 = join4Tuple $ break4Tuple v1 ^+^ break4Tuple v2++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , VectorSpace a, VectorSpace b, VectorSpace c, VectorSpace d+ ) => VectorSpace (a, b, c, d) where++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , InnerProductSpace a, InnerProductSpace b+ , InnerProductSpace c, InnerProductSpace d+ ) => InnerProductSpace (a, b, c, d) where+ v1 `dot` v2 = break4Tuple v1 `dot` break4Tuple v2++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , NormedSpace a, NormedSpace b, NormedSpace c, NormedSpace d+ ) => NormedSpace (a, b, c, d) where++++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , Groundring a ~ Groundring e+ , RModule a, RModule b, RModule c, RModule d, RModule e+ ) => RModule (a, b, c, d, e) where+ type Groundring (a, b, c, d, e) = Groundring a+ zeroVector = join5Tuple zeroVector+ a *^ v = join5Tuple $ a *^ (break5Tuple v)+ v1 ^+^ v2 = join5Tuple $ break5Tuple v1 ^+^ break5Tuple v2++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , Groundring a ~ Groundring e+ , VectorSpace a, VectorSpace b, VectorSpace c, VectorSpace d, VectorSpace e+ ) => VectorSpace (a, b, c, d, e) where++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , Groundring a ~ Groundring e+ , InnerProductSpace a, InnerProductSpace b, InnerProductSpace c+ , InnerProductSpace d, InnerProductSpace e+ ) => InnerProductSpace (a, b, c, d, e) where+ v1 `dot` v2 = break5Tuple v1 `dot` break5Tuple v2++instance+ ( Groundring a ~ Groundring b+ , Groundring a ~ Groundring c+ , Groundring a ~ Groundring d+ , Groundring a ~ Groundring e+ , NormedSpace a, NormedSpace b, NormedSpace c, NormedSpace d, NormedSpace e+ ) => NormedSpace (a, b, c, d, e) where+++-- * Vector spaces from arbitrary 'Fractional's++-- | Wrap an arbitrary 'Fractional' in this newtype+-- in order to get 'VectorSpace', and related instances.+newtype FractionalVectorSpace a = FractionalVectorSpace { getFractional :: a }+ deriving (Num, Fractional)+++instance Num a => RModule (FractionalVectorSpace a) where+ type Groundring (FractionalVectorSpace a) = a+ v1 ^+^ v2 = FractionalVectorSpace $ getFractional v1 + getFractional v2+ v ^* a = FractionalVectorSpace $ getFractional v * a+ zeroVector = FractionalVectorSpace 0++instance Fractional a => VectorSpace (FractionalVectorSpace a) where++instance Num a => InnerProductSpace (FractionalVectorSpace a) where+ v1 `dot` v2 = getFractional v1 * getFractional v2++instance Floating a => NormedSpace (FractionalVectorSpace a) where
− src/Data/VectorSpace/Fractional.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | VectorSpace instances for Num/Fractional types.------ This module includes instances for:------ * 'InnerProductSpace' and 'RModule' for 'Num'------ * 'VectorSpace' for 'Fractional's-module Data.VectorSpace.Fractional where---- These sometimes clash with user-defined instances.--- (See https://github.com/ivanperez-keera/dunai/issues/11, where this--- module used to be called Data.VectorSpace.Instances)--import Data.VectorSpace---- | R-module instance for any number, where '^+^ is '+' and multiplication is--- normal multiplication.-instance Num a => RModule a where- type Groundring a = a- zeroVector = 0- a *^ x = a * x- negateVector x = -x- x1 ^+^ x2 = x1 + x2- x1 ^-^ x2 = x1 - x2---- | Vector-space instance for any fractional, where vectorial division is--- normal number division.-instance Fractional a => VectorSpace a where- a ^/ x = a / x---- | Inner-product instance for any number.-instance Num a => InnerProductSpace a where- x1 `dot` x2 = x1 * x2
− src/Data/VectorSpace/Specific.hs
@@ -1,48 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | Vector space instances for concrete/specific types.------ This module contains:------ * 'RModule' instances for 'Int', 'Integer', 'Double' and 'Float'.------ * 'VectorSpace' for 'Double' and 'Float'.--module Data.VectorSpace.Specific where--import Data.VectorSpace---- | R-mobule instance for 'Int's.-instance RModule Int where- type Groundring Int = Int- (^+^) = (+)- (^*) = (*)- zeroVector = 0---- | R-mobule instance for 'Integer's.-instance RModule Integer where- type Groundring Integer = Integer- (^+^) = (+)- (^*) = (*)- zeroVector = 0----- | R-mobule instance for 'Double's.-instance RModule Double where- type Groundring Double = Double- (^+^) = (+)- (^*) = (*)- zeroVector = 0---- | R-mobule instance for 'Floating's.-instance RModule Float where- type Groundring Float = Float- (^+^) = (+)- (^*) = (*)- zeroVector = 0---- | Vector-space instance for 'Double'.-instance VectorSpace Double where---- | Vector-space instance for 'Floating's.-instance VectorSpace Float where
− src/Data/VectorSpace/Tuples.hs
@@ -1,98 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- | Vector space instances for small tuples of 'Fractional'.------ This module contains 'RModule', 'VectorSpace' and 'InnerProductSpace' for--- tuples of up to five elements.--module Data.VectorSpace.Tuples where--import Data.VectorSpace---- | R-module instance for tuples.-instance (Groundring a ~ Groundring b, RModule a, RModule b) => RModule (a, b) where- type Groundring (a, b) = Groundring a- zeroVector = (zeroVector, zeroVector)- (a, b) ^* x = (a ^* x, b ^* x)- (a1, b1) ^+^ (a2, b2) = (a1 ^+^ a2, b1 ^+^ b2)---- | Vector-space instance for tuples.-instance (Groundfield a ~ Groundfield b, VectorSpace a, VectorSpace b) => VectorSpace (a, b) where- (a, b) ^/ x = (a ^/ x, b ^/ x)---- | Inner Product Space instance for tuples.-instance (Groundfield a ~ Groundfield b, InnerProductSpace a, InnerProductSpace b) => InnerProductSpace (a, b) where- (a1, b1) `dot` (a2, b2) = (a1 `dot` a2) + (b1 `dot` b2)--{--instance Num a => RModule (a,a) where- type Groundring (a,a) = a- zeroVector = (0,0)- a *^ (x,y) = (a * x, a * y)- negateVector (x,y) = (-x, -y)- (x1,y1) ^+^ (x2,y2) = (x1 + x2, y1 + y2)- (x1,y1) ^-^ (x2,y2) = (x1 - x2, y1 - y2)--instance Fractional a => VectorSpace (a,a) where- (x,y) ^/ a = (x / a, y / a)---instance Fractional a => InnerProductSpace (a,a) where- (x1,y1) `dot` (x2,y2) = x1 * x2 + y1 * y2---}---- | R-module instance for tuples with 3 elements.-instance Num a => RModule (a,a,a) where- type Groundring (a,a,a) = a- zeroVector = (0,0,0)- a *^ (x,y,z) = (a * x, a * y, a * z)- negateVector (x,y,z) = (-x, -y, -z)- (x1,y1,z1) ^+^ (x2,y2,z2) = (x1+x2, y1+y2, z1+z2)- (x1,y1,z1) ^-^ (x2,y2,z2) = (x1-x2, y1-y2, z1-z2)---- | Vector-space instance for tuples with 3 elements.-instance Fractional a => VectorSpace (a,a,a) where- (x,y,z) ^/ a = (x / a, y / a, z / a)---- | Inner Product Space instance for tuples with 3 elements.-instance Num a => InnerProductSpace (a,a,a) where- (x1,y1,z1) `dot` (x2,y2,z2) = x1 * x2 + y1 * y2 + z1 * z2----- | R-module instance for tuples with 4 elements.-instance Num a => RModule (a,a,a,a) where- type Groundring (a,a,a,a) = a- zeroVector = (0,0,0,0)- a *^ (x,y,z,u) = (a * x, a * y, a * z, a * u)- negateVector (x,y,z,u) = (-x, -y, -z, -u)- (x1,y1,z1,u1) ^+^ (x2,y2,z2,u2) = (x1+x2, y1+y2, z1+z2, u1+u2)- (x1,y1,z1,u1) ^-^ (x2,y2,z2,u2) = (x1-x2, y1-y2, z1-z2, u1-u2)---- | Vector-space instance for tuples with 4 elements.-instance Fractional a => VectorSpace (a,a,a,a) where- (x,y,z,u) ^/ a = (x / a, y / a, z / a, u / a)---- | Inner Product Space instance for tuples with 4 elements.-instance Num a => InnerProductSpace (a,a,a,a) where- (x1,y1,z1,u1) `dot` (x2,y2,z2,u2) = x1 * x2 + y1 * y2 + z1 * z2 + u1 * u2----- | R-module instance for tuples with 5 elements.-instance Num a => RModule (a,a,a,a,a) where- type Groundring (a,a,a,a,a) = a- zeroVector = (0,0,0,0,0)- a *^ (x,y,z,u,v) = (a * x, a * y, a * z, a * u, a * v)- negateVector (x,y,z,u,v) = (-x, -y, -z, -u, -v)- (x1,y1,z1,u1,v1) ^+^ (x2,y2,z2,u2,v2) = (x1+x2, y1+y2, z1+z2, u1+u2, v1+v2)- (x1,y1,z1,u1,v1) ^-^ (x2,y2,z2,u2,v2) = (x1-x2, y1-y2, z1-z2, u1-u2, v1-v2)---- | Vector-space instance for tuples with 5 elements.-instance Fractional a => VectorSpace (a,a,a,a,a) where- (x,y,z,u,v) ^/ a = (x / a, y / a, z / a, u / a, v / a)---- | Inner Product Space instance for tuples with 5 elements.-instance Num a => InnerProductSpace (a,a,a,a,a) where- (x1,y1,z1,u1,v1) `dot` (x2,y2,z2,u2,v2) =- x1 * x2 + y1 * y2 + z1 * z2 + u1 * u2 + v1 * v2