packages feed

dunai-core (empty) → 0.5.1.0

raw patch · 25 files changed

+2270/−0 lines, 25 filesdep +MonadRandomdep +basedep +transformerssetup-changed

Dependencies added: MonadRandom, base, transformers, transformers-base

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016, Ivan Perez and Manuel Bärenz++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Ivan Perez and Manuel Bärenz nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dunai-core.cabal view
@@ -0,0 +1,83 @@+name:                dunai-core+version:             0.5.1.0+synopsis:            Generalised reactive framework supporting classic, arrowized and monadic FRP.+                     (Core library fork.)+homepage:            https://github.com/turion/dunai-core+description:+  Dunai is a DSL for strongly-typed CPS-based composable transformations.+  .+  Dunai is based on a concept of Monadic Stream Functions (MSFs). MSFs are+  transformations 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,+  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 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.+  .+  MSFs 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:+  <http://dl.acm.org/citation.cfm?id=2976010>+  (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).+license:             BSD3+license-file:        LICENSE+author:              Ivan Perez, Manuel Bärenz+maintainer:          programming@manuelbaerenz.de+-- copyright:+category:            Reactivity, FRP+build-type:          Simple+-- extra-source-files:+cabal-version:       1.18++source-repository head+  type:     git+  location: git@github.com:turion/dunai-core+++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+                     Data.MonadicStreamFunction.Core+                     Data.MonadicStreamFunction.Async+                     Data.MonadicStreamFunction.Instances.ArrowChoice+                     Data.MonadicStreamFunction.Instances.ArrowLoop+                     Data.MonadicStreamFunction.Instances.ArrowPlus+                     Data.MonadicStreamFunction.Instances.Num+                     Data.MonadicStreamFunction.Instances.VectorSpace+                     Data.MonadicStreamFunction.Parallel+                     Data.MonadicStreamFunction.ReactHandle+                     Data.MonadicStreamFunction.Util++                     -- Auxiliary definitions+                     Data.VectorSpace++  other-modules:     Control.Arrow.Util++  build-depends:     base >=4.10 && < 4.13+                   , transformers == 0.5.*+                   , transformers-base == 0.4.*+                   , MonadRandom == 0.5.*+  hs-source-dirs:    src+  default-language:  Haskell2010+  ghc-options:       -Wall
+ src/Control/Arrow/Util.hs view
@@ -0,0 +1,28 @@+-- | Utility functions to work with 'Arrow's.+module Control.Arrow.Util where++import Control.Arrow++-- | Constantly produce the same output.+constantly :: Arrow a => b -> a c b+constantly = arr . const+{-# INLINE constantly #-}++-- import Control.Category (id)+-- import Prelude hiding (id)++-- (&&&!) :: Arrow a => a b c -> a b () -> a b c+-- a1 &&&! a2 = (a1 &&& a2) >>> arr fst++-- sink :: Arrow a => a b c -> a c () -> a b c+-- a1 `sink` a2 = a1 >>> (id &&& a2) >>> arr fst++-- * Apply functions at the end.+--+-- | Alternative name for '^<<'.+elementwise :: Arrow a => (c -> d) -> a b c -> a b d+elementwise = (^<<)++-- | Apply a curried function with two arguments to the outputs of two arrows.+elementwise2 :: Arrow a => (c -> d -> e) -> a b c -> a b d -> a b e+elementwise2 op a1 a2 = (a1 &&& a2) >>^ uncurry op
+ src/Control/Monad/Trans/MSF.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE Rank2Types #-}++-- | 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.Maybe+    , module Control.Monad.Trans.MSF.Random+    , module Control.Monad.Trans.MSF.Reader+    , module Control.Monad.Trans.MSF.State+    , module Control.Monad.Trans.MSF.Writer+    )+  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+import Control.Monad.Trans.MSF.Reader+import Control.Monad.Trans.MSF.State+import Control.Monad.Trans.MSF.Writer
+ src/Control/Monad/Trans/MSF/Except.hs view
@@ -0,0 +1,231 @@+{-# LANGUAGE Arrows              #-}+{-# LANGUAGE Rank2Types          #-}+-- | 'MSF's in the 'ExceptT' monad are monadic stream functions+--   that can throw exceptions,+--   i.e. return an exception value instead of a continuation.+--   This module gives ways to throw exceptions in various ways,+--   and to handle them through a monadic interface.+module Control.Monad.Trans.MSF.Except+  ( module Control.Monad.Trans.MSF.Except+  , module Control.Monad.Trans.Except+  ) where++-- External++import           Control.Applicative+import qualified Control.Category           as Category+import           Control.Monad              (liftM, ap)+import           Control.Monad.Trans.Class+import           Control.Monad.Trans.Except hiding (liftCallCC, liftListen, liftPass) -- Avoid conflicting exports+import           Control.Monad.Trans.Maybe+import           Data.Either                (fromLeft, fromRight)++-- Internal+-- import Control.Monad.Trans.MSF.GenLift+import Data.MonadicStreamFunction++-- * Throwing exceptions++-- | Throw the exception 'e' whenever the function evaluates to 'True'.+throwOnCond :: Monad m => (a -> Bool) -> e -> MSF (ExceptT e m) a a+throwOnCond cond e = proc a -> if cond a+  then throwS  -< e+  else returnA -< a++-- | Variant of 'throwOnCond' for Kleisli arrows.+-- | Throws the exception when the input is 'True'.+throwOnCondM :: Monad m => (a -> m Bool) -> e -> MSF (ExceptT e m) a a+throwOnCondM cond e = proc a -> do+  b <- arrM (lift . cond) -< a+  if b+    then throwS  -< e+    else returnA -< a++-- | Throw the exception when the input is 'True'.+throwOn :: Monad m => e -> MSF (ExceptT e m) Bool ()+throwOn e = proc b -> throwOn' -< (b, e)++-- | Variant of 'throwOn', where the exception may change every tick.+throwOn' :: Monad m => MSF (ExceptT e m) (Bool, e) ()+throwOn' = proc (b, e) -> if b+  then throwS  -< e+  else returnA -< ()++-- | 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++-- | Immediately throw the incoming exception.+throwS :: Monad m => MSF (ExceptT e m) e a+throwS = arrM throwE++-- | Immediately throw the given exception.+throw :: Monad m => e -> MSF (ExceptT e m) a b+throw = arrM_ . throwE++-- | Do not throw an exception.+pass :: Monad m => MSF (ExceptT e m) a a+pass = Category.id++-- | Converts an 'MSF' in 'MaybeT' to an 'MSF' in 'ExceptT'.+--   Whenever 'Nothing' is thrown, throw @()@ instead.+maybeToExceptS :: Monad m+               => MSF (MaybeT m) a b -> MSF (ExceptT () m) a b+maybeToExceptS = liftMSFPurer (ExceptT . (maybe (Left ()) Right <$>) . runMaybeT)++-- * Catching exceptions++-- | Catch an exception in an 'MSF'. As soon as an exception occurs,+--   the current continuation is replaced by a new 'MSF', the exception handler,+--   based on the exception value.+--   For exception catching where the handler can throw further exceptions,+--   see 'MSFExcept' further below.+catchS :: Monad m => MSF (ExceptT e m) a b -> (e -> MSF m a b) -> MSF m a b+catchS msf f = safely $ do+  e <- try msf+  safe $ f e++-- | Similar to Yampa's delayed switching. Looses 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+  inExceptT -< ExceptT $ return $ maybe (Right b) Left me++-- | 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')++-- | Embed an 'ExceptT' value inside the 'MSF'.+--   Whenever the input value is an ordinary value,+--   it is passed on. If it is an exception, it is raised.+inExceptT :: Monad m => MSF (ExceptT e m) (ExceptT e m a) a+inExceptT = arrM id++-- | In case an exception occurs in the first argument,+--   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+  _       <- try $ msf <<< arr fst+  (_, e2) <- currentInput+  return e2+++-- * Monad interface for Exception MSFs++-- | 'MSF's with an 'ExceptT' transformer layer+--   are in fact monads /in the exception type/.+--+--   * 'return' corresponds to throwing an exception immediately.+--   * '>>=' 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.+-- Execute an 'MSF' in 'ExceptT' until it raises an exception.+try :: MSF (ExceptT e m) a b -> MSFExcept m a b e+try = MSFExcept++-- | Immediately throw the current input as an exception.+currentInput :: Monad m => MSFExcept m e b e+currentInput = try throwS++-- | Functor instance for MSFs on the 'Either' monad. Fmapping is the same as+-- applying a transformation to the 'Left' values.+instance Monad m => Functor (MSFExcept m a b) where+  fmap = liftM++-- | Applicative instance for MSFs on the 'Either' monad. The function 'pure'+-- throws an exception.+instance Monad m => Applicative (MSFExcept m a b) where+  pure = MSFExcept . throw+  (<*>) = ap++-- | 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)++-- | 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+  where+    safely' msf = MSF $ \a -> do+      (b, msf') <- fromRight (error "safely: Received `Left`")+        <$> (runExceptT $ unMSF msf a)+      return (b, safely' msf')++-- | 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++-- | Inside the 'MSFExcept' monad, execute an action of the wrapped monad.+--   This passes the last input value to the action,+--   but doesn't advance a tick.+once :: Monad m => (a -> m e) -> MSFExcept m a b e+once f = try $ arrM (lift . f) >>> throwS++-- | Variant of 'once' without input.+once_ :: Monad m => m e -> MSFExcept m a b e+once_ = once . const++-- | Advances a single tick with the given Kleisli arrow,+--   and then throws an exception.+step :: Monad m => (a -> m (b, e)) -> MSFExcept m a b e+step f = try $ proc a -> do+  n      <- count           -< ()+  (b, e) <- arrM (lift . f) -< a+  _      <- throwOn'        -< (n > (1 :: Int), e)+  returnA                   -< b++-- * Utilities definable in terms of 'MSFExcept'++-- TODO This is possibly not the best location for these functions,+-- but moving them to Data.MonadicStreamFunction.Util would form an import cycle+-- that could only be broken by moving a few things to Data.MonadicStreamFunction.Core+-- (that probably belong there anyways).++-- | 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+-- first one).+performOnFirstSample :: Monad m => m (MSF m a b) -> MSF m a b+performOnFirstSample sfaction = safely $ do+  msf <- once_ sfaction+  safe msf++-- | Reactimates an 'MSFExcept' until it throws an exception.+reactimateExcept :: Monad m => MSFExcept m () () e -> m e+reactimateExcept msfe = fromLeft (error "reactimateExcept: Received `Right`")+  <$> (runExceptT $ reactimate $ runMSFExcept msfe)++-- | Reactimates an 'MSF' until it returns 'True'.+reactimateB :: Monad m => MSF m () Bool -> m ()+reactimateB sf = reactimateExcept $ try $ liftMSFTrans sf >>> throwOn ()
+ src/Control/Monad/Trans/MSF/GenLift.hs view
@@ -0,0 +1,138 @@+{-# 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, 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
@@ -0,0 +1,121 @@+{-# LANGUAGE Arrows              #-}+{-# LANGUAGE Rank2Types          #-}+{- |+The 'Maybe' monad is very versatile. It can stand for default arguments,+for absent values, and for (nondescript) exceptions.+The latter viewpoint is most natural in the context of 'MSF's.+-}+module Control.Monad.Trans.MSF.Maybe+  ( module Control.Monad.Trans.MSF.Maybe+  , module Control.Monad.Trans.Maybe+  , maybeToExceptS+  ) where++-- External+import Control.Monad.Trans.Maybe+  hiding (liftCallCC, liftCatch, liftListen, liftPass) -- Avoid conflicting exports++-- 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++-- | Throw the exception when the condition becomes true on the input.+exitWhen :: Monad m => (a -> Bool) -> MSF (MaybeT m) a a+exitWhen condition = proc a -> do+  _ <- exitIf -< condition a+  returnA     -< a++-- | Exit when the incoming value is 'True'.+exitIf :: Monad m => MSF (MaybeT m) Bool ()+exitIf = proc condition -> if condition+  then exit    -< ()+  else returnA -< ()++-- | @Just a@ is passed along, 'Nothing' causes the whole 'MSF' to exit.+maybeExit :: Monad m => MSF (MaybeT m) (Maybe a) a+maybeExit = inMaybeT++-- | Embed a 'Maybe' value in the 'MaybeT' layer. Identical to 'maybeExit'.+inMaybeT :: Monad m => MSF (MaybeT m) (Maybe a) a+inMaybeT = arrM $ MaybeT . return+++-- * Catching Maybe exceptions++-- | 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+  inMaybeT -< if c then Nothing else Just b++-- | When an exception occurs in the first 'msf', the second 'msf' is executed from there.+catchMaybe+  :: Monad m+  => MSF (MaybeT m) a b -> MSF m a b -> MSF m a b+catchMaybe msf1 msf2 = safely $ do+  _ <- try $ maybeToExceptS msf1+  safe msf2++-- * Converting to and from 'MaybeT'++-- | Converts a list to an 'MSF' in 'MaybeT',+--   which outputs an element of the list at each step,+--   throwing 'Nothing' when the list ends.+listToMaybeS :: Monad m => [b] -> MSF (MaybeT m) a b+listToMaybeS = foldr iPost exit++-- * 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+  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)++-- | 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+  :: 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+-- combine effects and streams (i.e., for testing purposes).+embed_ :: Monad m => MSF m a () -> [a] -> m ()++embed_ msf as = reactimateMaybe $ listToMaybeS as >>> liftMSFTrans msf
+ src/Control/Monad/Trans/MSF/RWS.hs view
@@ -0,0 +1,45 @@+-- | 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 Control.Monad.Trans.MSF.GenLift+import Data.MonadicStreamFunction++-- * 'RWS' (Reader-Writer-State) monad++-- | Run the 'RWST' layer by making the state variables explicit.+runRWSS :: (Monad m, Monoid w)+        => MSF (RWST r w s m) a b+        -> MSF m (r, s, a) (w, s, b)+runRWSS = transS transformInput transformOutput+  where+    transformInput  (_, _, a) = return a+    transformOutput (r, s, _) msfaction = sym <$> runRWST msfaction r s+    sym ((b, msf'), s, w) = ((w, s, b), msf')++-- | Wrap an 'MSF' with explicit state variables in 'RWST' monad.+rwsS :: (Monad m, Monoid w)+     => MSF m (r, s, a) (w, s, b)+     -> MSF (RWST r w s m) a b+rwsS = lifterS wrapRWST+  where+    wrapRWST :: Monad m+             => ((r, s, a) -> m ((w, s, b), c)) -> a -> RWST r w s m (b, c)+    wrapRWST f a = RWST $ \r s -> do+      ((w, s', b), c) <- f (r, s, a)+      return ((b, c), s', w)
+ src/Control/Monad/Trans/MSF/Random.hs view
@@ -0,0 +1,74 @@+-- | 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+  (+    runRandS+  , evalRandS++  , getRandomS+  , getRandomsS+  , getRandomRS+  , getRandomRS_+  , getRandomsRS+  , getRandomsRS_+  ) where++-- External+import Control.Monad.Random++-- Internal+import Data.MonadicStreamFunction++-- | Run an 'MSF' in the 'RandT' random number monad transformer+--   by supplying an initial random generator.+--   Updates the generator every step.+runRandS :: (RandomGen g, 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')++-- | 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, 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+++-- | Create a stream of lists of random values.+getRandomsS :: (MonadRandom m, Random b) => MSF m a [b]+getRandomsS = arrM_ 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++-- | 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++-- | 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
@@ -0,0 +1,109 @@+{-# LANGUAGE Rank2Types          #-}++-- | 'MSF's with a 'Reader' monadic layer.+--+-- 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' running and wrapping.+  , readerS+  , runReaderS+  , runReaderS_+  -- ** Alternative implementation using internal type.+  , readerS'+  , runReaderS'+  -- ** Alternative implementation using generic lifting.+  , runReaderS''+  ) where++-- External+import Control.Monad.Trans.Reader+  hiding (liftCallCC, liftCatch) -- Avoid conflicting exports++-- Internal+import Control.Monad.Trans.MSF.GenLift+import Data.MonadicStreamFunction++-- * Reader 'MSF' running and wrapping++-- | 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')++-- | 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')+++-- | 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
+ src/Control/Monad/Trans/MSF/State.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE Rank2Types #-}+-- | 'MSF's with a 'State' monadic layer.+--+-- 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 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++-- Internal+import Control.Monad.Trans.MSF.GenLift+import Data.MonadicStreamFunction++-- * 'State' 'MSF' running and wrapping++-- | 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')++-- | 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')++-- | 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' :: 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' :: 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'' :: 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'.++-- | Alternative implementation of 'runStateS' using 'transG'.+runStateS''' :: 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)
+ src/Control/Monad/Trans/MSF/Writer.hs view
@@ -0,0 +1,104 @@+-- | 'MSF's with a 'Writer' monadic layer.+--+-- 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 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.Monoid++-- Internal+import Control.Monad.Trans.MSF.GenLift+import Data.MonadicStreamFunction++-- * 'Writer' 'MSF' running and wrapping++-- | 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')++-- | 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, 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, 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)
+ src/Data/MonadicStreamFunction.hs view
@@ -0,0 +1,59 @@+-- | 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.+--+--   See the module "Data.MonadicStreamFunction.Core" for details.+--+--   '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 '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.+--+--   '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 'MSF's, see:+--   <http://dl.acm.org/citation.cfm?id=2976010>+--   (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).+--+--   Apart from the modules exported, this module exports instances from:+--+--   - "Data.MonadicStreamFunction.Instances.ArrowChoice"+--   - "Data.MonadicStreamFunction.Instances.ArrowLoop"+--   - "Data.MonadicStreamFunction.Instances.ArrowPlus"++module Data.MonadicStreamFunction+  ( module Control.Arrow+  , module Data.MonadicStreamFunction.Core+  , module Data.MonadicStreamFunction.Util+  )+ where++-- External++import Control.Arrow++-- Internal++import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.Util++-- Internal (Instances)++import Data.MonadicStreamFunction.Instances.ArrowChoice ()+import Data.MonadicStreamFunction.Instances.ArrowLoop   ()+import Data.MonadicStreamFunction.Instances.ArrowPlus   ()
+ src/Data/MonadicStreamFunction/Async.hs view
@@ -0,0 +1,65 @@+-- | This module contains operations on monadic streams that are asynchronous,+--   i.e. that change the speed at which data enters or leaves the 'MSF'.++module Data.MonadicStreamFunction.Async where++-- Internal+import Data.MonadicStreamFunction.Core+import Data.MonadicStreamFunction.Util (MStream)++{- |+Concatenates a monadic stream of lists to a monadic stream.+The stream of lists will be called exactly when new data is needed.++Example:++>>> let intstream = arrM_ $ putStrLn "Enter a list of Ints:" >> readLn :: MStream IO [Int]+>>> reactimate $ concatS intstream >>> arrM print+Enter a list of Ints:+[1,2,33]+1+2+33+Enter a list of Ints:+[]+Enter a list of Ints:+[]+Enter a list of Ints:+[1,2]+1+2+Enter a list of Ints:+...++Beware that @concatS msf@ becomes unproductive when @msf@ starts outputting empty lists forever.+This is ok:++>>> let boolToList b = if b then ["Yes"] else []+>>> let everyOddEmpty = count >>> arr (even >>> boolToList)+>>> reactimate $ concatS everyOddEmpty >>> arrM print+"Yes"+"Yes"+"Yes"+"Yes"+"Yes"+...++But this will be caught in a loop:++>>> let after3Empty = count >>> arr ((<= 3) >>> boolToList)+>>> reactimate $ concatS after3Empty  >>> arrM print+"Yes"+"Yes"+"Yes"+^CInterrupted.+-}+concatS :: Monad m => MStream m [b] -> MStream m b+concatS msf = MSF $ \_ -> tick msf []+  where+    tick msf' (b:bs) = return (b, MSF $ \_ -> tick msf' bs)+    tick msf' []     = do+      (bs, msf'') <- unMSF msf' ()+      tick msf'' bs+-- TODO Maybe this can be written more nicely with exceptions?+-- Similarly takeS :: Int -> MSF m a b -> MSFExcept m a b () throws an exception after n ticks+-- Or with merge?
+ src/Data/MonadicStreamFunction/Core.hs view
@@ -0,0 +1,230 @@+{-# 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.+--+--   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 '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.+--+--   '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 '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++-- 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')++-- | 'Arrow' instance for 'MSF's.+instance Monad m => Arrow (MSF m) where++  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' instance for 'MSF's.+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)++-- | 'Applicative' instance for 'MSF's.+instance 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 'MSF's++-- ** Lifting point-wise computations++-- | 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)++-- | Monadic lifting from one monad into another+liftS :: (Monad m2, MonadBase m1 m2) => (a -> m1 b) -> MSF m2 a b+liftS = arrM . (liftBase .)++-- ** Lifting 'MSF's++-- *** Lifting across monad stacks++-- | 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++-- | 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 'MSF' Lifting++-- 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+--+-- Then we can express:+--+-- liftMSFTrans = liftMSFPurer lift+-- liftMSFBase  = liftMSFPurer liftBase+--+-- We could also define a strict version of liftMSFPurer as follows:+--+-- liftMStreamPurer' f = liftMSFPurer (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'
+ src/Data/MonadicStreamFunction/Instances/ArrowChoice.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE InstanceSigs         #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | Instance of 'ArrowChoice' for Monadic Stream Functions ('MSF').+--+--   Import this module to include that (orphan) instance.+module Data.MonadicStreamFunction.Instances.ArrowChoice where++import Control.Arrow++import Data.MonadicStreamFunction.Core++-- | 'ArrowChoice' instance for MSFs.+instance Monad m => ArrowChoice (MSF m) where+  left :: MSF m a b -> MSF m (Either a c) (Either b c)+  left sf = MSF f+    where+      f (Left a) = do (b, sf') <- unMSF sf a+                      return (Left b, left sf')+      f (Right c) = return (Right c, left sf)
+ src/Data/MonadicStreamFunction/Instances/ArrowLoop.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE InstanceSigs         #-}+{-# LANGUAGE RecursiveDo          #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | Instance of 'ArrowLoop' for Monadic Stream Functions ('MSF').+--+--   Import this module to include that (orphan) instance.+--+--   This is only defined for monads that are instances of 'MonadFix'.+module Data.MonadicStreamFunction.Instances.ArrowLoop where++import Data.MonadicStreamFunction.Core++-- External+import Control.Arrow+import Control.Monad.Fix++-- | 'ArrowLoop' instance for MSFs. The monad must be an instance of+-- 'MonadFix'.+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)+              return (b, loop sf')
+ src/Data/MonadicStreamFunction/Instances/ArrowPlus.hs view
@@ -0,0 +1,29 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | Instance of 'ArrowPlus' for Monadic Stream Functions ('MSF').+--+--   Import this module to include that (orphan) instance.+--+--   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++-- | Instance of 'ArrowZero' for Monadic Stream Functions ('MSF').+--   The monad must be an instance of 'MonadPlus'.+instance (Monad m, MonadPlus m) => ArrowZero (MSF m) where+  zeroArrow = MSF $ const mzero++-- | Instance of 'ArrowPlus' for Monadic Stream Functions ('MSF').+--   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 (Monad m, MonadPlus m) => Alternative (MSF m a) where+  empty = zeroArrow+  (<|>) = (<+>)
+ src/Data/MonadicStreamFunction/Instances/Num.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE TypeFamilies         #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- | 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+-- msf2 :: MSF Input Double -- defined however you want+-- msf3 :: MSF Input Double+-- msf3 = msf1 + msf2+-- @+--+-- instead of+--+-- @+-- msf3 = (msf1 &&& msf2) >>> arr (uncurry (+))+-- @+--+-- Instances are provided for the type classes 'Num', 'Fractional'+-- and 'Floating'.++module Data.MonadicStreamFunction.Instances.Num where++import Control.Arrow.Util+import Data.MonadicStreamFunction.Core++-- | 'Num' instance for 'MSF's.+instance (Monad m, Num b) => Num (MSF m a b) where+  (+)         = elementwise2 (+)+  (-)         = elementwise2 (-)+  (*)         = elementwise2 (*)+  abs         = elementwise abs+  signum      = elementwise signum+  negate      = elementwise negate+  fromInteger = constantly . fromInteger++-- | '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 'MSF's.+instance (Monad m, Floating b) => Floating (MSF m a b) where+  pi      = constantly   pi+  exp     = elementwise  exp+  log     = elementwise  log+  sqrt    = elementwise  sqrt+  (**)    = elementwise2 (**)+  logBase = elementwise2 logBase+  sin     = elementwise  sin+  cos     = elementwise  cos+  tan     = elementwise  tan+  asin    = elementwise  asin+  acos    = elementwise  acos+  atan    = elementwise  atan+  sinh    = elementwise  sinh+  cosh    = elementwise  cosh+  tanh    = elementwise  tanh+  asinh   = elementwise  asinh+  acosh   = elementwise  acosh+  atanh   = elementwise  atanh
+ src/Data/MonadicStreamFunction/Instances/VectorSpace.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE TypeFamilies         #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | '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:+--+-- @+-- msf1 :: MSF Input (Double, Double) -- defined however you want+-- msf2 :: MSF Input (Double, Double) -- defined however you want+-- msf3 :: MSF Input (Double, Double)+-- msf3 = msf1 ^+^ msf2+-- @+--+-- instead of+--+-- @+-- msf3 = (msf1 &&& msf2) >>> arr (uncurry (^+^))+-- @+--+--+-- Instances are provided for the type classes 'RModule' and 'VectorSpace'.+module Data.MonadicStreamFunction.Instances.VectorSpace where++import Control.Arrow+import Control.Arrow.Util+import Data.MonadicStreamFunction.Core+import Data.VectorSpace++-- These conflict with Data.VectorSpace.Instances++-- | 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+  r *^ msf     = msf >>^ (r *^)+  negateVector = (>>^ negateVector)+  (^+^)        = elementwise2 (^+^)+  (^-^)        = elementwise2 (^-^)++-- | Vector-space instance for 'MSF's.+instance (Monad m, VectorSpace v) => VectorSpace (MSF m a v) where+  msf ^/ r = msf >>^ (^/ r)
+ src/Data/MonadicStreamFunction/Parallel.hs view
@@ -0,0 +1,23 @@+-- | Versions of arrow combinators that run things in parallel using 'par', if+-- possible.+module Data.MonadicStreamFunction.Parallel where++-- External+import Control.Arrow+import GHC.Conc++-- Internal+import Data.MonadicStreamFunction++-- | 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+  (b, msf1') <- unMSF msf1 a+  (d, msf2') <- unMSF msf2 c+  b `par` d `pseq` return ((b, d), msf1' *|* msf2')++-- | 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
@@ -0,0 +1,34 @@+-- | '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.++module Data.MonadicStreamFunction.ReactHandle where++-- External+import Control.Monad.IO.Class+import Data.IORef++-- Internal+import Data.MonadicStreamFunction+++-- | 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.+reactInit :: MonadIO m => MSF m () () -> m (ReactHandle m)+reactInit = liftIO . newIORef+++-- | 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'
+ src/Data/MonadicStreamFunction/Util.hs view
@@ -0,0 +1,235 @@+{-# LANGUAGE Arrows #-}+-- | Useful auxiliary functions and definitions.+module Data.MonadicStreamFunction.Util where++-- External+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.VectorSpace++-- * Streams and sinks++-- | 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 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 (^>>>) #-}++-- | 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+-- '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+  Just a  -> arr Just <<< msf -< a+  Nothing -> returnA          -< Nothing++-- * Adding side effects++-- | Applies a function to produce an additional side effect and passes the+-- input unchanged.+withSideEffect :: Monad m => (a -> m b) -> MSF m a a+withSideEffect method = (id &&& arrM method) >>> arr fst++-- | Produces an additional side effect and passes the input unchanged.+withSideEffect_ :: Monad m => m b -> MSF m a a+withSideEffect_ method = withSideEffect $ const method++-- * Delays++-- See also: 'iPre'++-- | 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)++-- | 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++-- | Buffers and returns the elements in FIFO order,+--   returning 'Nothing' whenever the buffer is empty.+fifo :: Monad m => MSF m [a] (Maybe a)+fifo = feedback [] $ proc (as, accum) -> do+  let accum' = accum ++ as+  returnA -< case accum' of+    []       -> (Nothing, [])+    (a : as) -> (Just a , as)++-- * Edge detectors++-- | Emits 'True' (once) when the input value changes+--   to the given argument, from any other value.+--+--   (If the input is equal to the given argument on the first tick,+--    'True' is also emitted.+--   )+edgeTo+  :: (Monad m, Eq a)+  => a -- ^ The new value that the signal should have _now_ to trigger the edge+  -> MSF m a Bool+edgeTo aNew = proc a -> do+  maPrevious <- delay Nothing -< Just a+  returnA                     -< a == aNew && maPrevious /= Just aNew++-- | Like 'edgeTo', but triggers as soon when the input changes+--   from the given argument to any value that is _not_ equal to it.+--+--   (Does not trigger on the first tick.)+edgeFrom+  :: (Monad m, Eq a)+	=> a -- ^ The old value that the signal should have directly before the edge+  -> MSF m a Bool+edgeFrom aOld = proc a -> do+  maPrevious <- delay Nothing -< Just a+  returnA                     -< a /= aOld && maPrevious == Just aOld++-- | Triggers when both 'edgeTo' and 'edgeFrom' would trigger,+--   i.e. when the input changes from the first given value to the second.+edgeFromTo+  :: (Monad m, Eq a)+	=> a -- ^ The old value that the signal should have directly before the edge+  -> a -- ^ The new value that the signal should have _now_ to trigger the edge+  -> MSF m a Bool+edgeFromTo aOld aNew = edgeFrom aOld &&& edgeTo aNew >>> arr (uncurry (&&))++-- | Emits 'True' (once) when the input value evaluates to 'True'+--   under the given predicate.+--+--   Example usage: @edgeWhen (> 1)@+edgeWhen+  :: (Monad m, Eq a)+	=> (a -> Bool) -- ^ The predicate that is to be evaluated on the incoming signal+  -> MSF m a Bool+edgeWhen predicate  = arr predicate >>> edgeTo True++++-- * Folding++-- ** Folding for 'VectorSpace' instances++-- | Count the number of simulation steps. Produces 1, 2, 3,...+count :: (Num n, Monad m) => MSF m a n+count = arr (const 1) >>> accumulateWith (+) 0++-- | Sums the inputs, starting from zero.+sumS :: (RModule v, Monad m) => MSF m v v+sumS = sumFrom zeroVector++-- | Sums the inputs, starting from an initial vector.+sumFrom :: (RModule v, Monad m) => v -> MSF m v v+sumFrom = accumulateWith (^+^)++-- ** Folding for monoids++-- | Accumulate the inputs, starting from 'mempty'.+mappendS :: (Monoid n, Monad m) => MSF m n n+mappendS = mappendFrom mempty+{-# INLINE mappendS #-}++-- | Accumulate the inputs, starting from an initial monoid value.+mappendFrom :: (Monoid n, Monad m) => n -> MSF m n n+mappendFrom = accumulateWith mappend++-- ** Generic folding \/ accumulation++-- | 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++-- | Generate outputs using a step-wise generation function and an initial+-- value.+unfold :: Monad m => (a -> (b, a)) -> a -> MSF m arbitrary b+unfold f a = feedback a (arr (snd >>> f))++-- | Generate outputs using a step-wise generation Kleisli arrow and an initial+-- value.+unfoldM :: Monad m => (a -> m (b, a)) -> a -> MSF m arbitrary b+unfoldM f a = feedback a (arrM (snd >>> f))++-- | Generate outputs using a step-wise generation function and an initial+-- value. Version of 'unfold' in which the output and the new accumulator+-- are the same. Should be equal to @\f a -> unfold (f >>> dup) a@.+repeatedly :: Monad m => (a -> a) -> a -> MSF m () a+repeatedly f = unfold $ f >>> dup+  where+    dup a = (a, a)+++-- * Debugging++-- | Outputs every input sample, with a given message prefix.+trace :: Show a => String -> MSF IO a a+trace = traceWith putStrLn++-- | Outputs every input sample, with a given message prefix, using an+-- auxiliary printing function.+traceWith :: (Monad m, Show a) => (String -> m ()) -> String -> MSF m a a+traceWith method msg =+  withSideEffect (method . (msg ++) . show)++-- | Outputs every input sample, with a given message prefix, using an+-- auxiliary printing function, when a condition is met.+traceWhen :: (Monad m, Show a) => (a -> Bool) -> (String -> m ()) -> String -> MSF m a a+traceWhen cond method msg = withSideEffect $ \a ->+  when (cond a) $ method $ msg ++ show a++-- | Outputs every input sample, with a given message prefix, when a condition+-- is met, and waits for some input \/ enter to continue.+pauseOn :: Show a => (a -> Bool) -> String -> MSF IO a a+pauseOn cond = traceWhen cond $ \s -> print s >> getLine >> return ()
+ src/Data/VectorSpace.hs view
@@ -0,0 +1,323 @@+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+-- |+-- Module      :  Data.VectorSpace+-- Copyright   :  (c) Ivan Perez and Manuel Bärenz+-- License     :  See the LICENSE file in the distribution.+--+-- Maintainer  :  ivan.perez@keera.co.uk+-- Stability   :  provisional+-- Portability :  non-portable (GHC extensions)+--+-- Vector space type relation and basic instances.+-- Heavily inspired by Yampa's @FRP.Yampa.VectorSpace@ module.++module Data.VectorSpace where++------------------------------------------------------------------------------+-- * Vector space classes+------------------------------------------------------------------------------++infixr 6 *^+infixl 6 ^/+infix 6 `dot`+infixl 5 ^+^, ^-^++-- | R-modules.+--   A module @v@ over a ring @Groundring v@+--   is an abelian group with a linear multiplication.+--   The hat @^@ denotes the side of an operation+--   on which the vector stands,+--   i.e. @a *^ v@ for @v@ a vector.+--+-- A minimal definition should include the type 'Groundring' and the+-- implementations of 'zeroVector', '^+^', and one of '*^' or '^*'.+--+--   The following laws must be satisfied:+--+--   * @v1 ^+^ v2 == v2 ^+^ v1@+--   * @a *^ zeroVector == zeroVector@+--   * @a *^ (v1 ^+^ v2) == a *^ v1 ^+^ a*^ v2+--   * @a *^ v == v ^* a@+--   * @negateVector v == (-1) *^ v@+--   * @v1 ^-^ v2 == v1 ^+^ negateVector v2@+class Num (Groundring v) => RModule v where+    type Groundring v+    zeroVector   :: v++    (*^)         :: Groundring v -> v -> v+    (*^)         = flip (^*)++    (^*)         :: v -> Groundring v -> v+    (^*)         = flip (*^)++    negateVector :: v -> v+    negateVector v = (-1) *^ v++    (^+^)        :: v -> v -> v++    (^-^)        :: v -> v -> v+    v1 ^-^ v2     = v1 ^+^ negateVector v2++-- Maybe norm and normalize should not be class methods, in which case+-- the constraint on the coefficient space (a) should (or, at least, could)+-- be Fractional (roughly a Field) rather than Floating.++-- Minimal instance: zeroVector, (*^), (^+^), dot+-- class Fractional (Groundfield v) => VectorSpace v where++-- | A vector space is a module over a field,+--   i.e. a commutative ring with inverses.+--+--   It needs to satisfy the axiom+--   @v ^/ a == (1/a) *^ v@,+--   which is the default implementation.+class (Fractional (Groundring v), RModule v) => VectorSpace v where+    (^/) :: v -> Groundfield v -> v+    v ^/ a = (1/a) *^ v++-- | 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 Groundfield v = Groundring v++-- | An inner product space is a module with an inner product,+--   i.e. a map @dot@ satisfying+--+--   * @v1 `dot` v2 == v2 `dot` v1@+--   * @(v1 ^+^ v2) `dot` v3 == v1 `dot` v3 ^+^ v2 `dot` v3@+--   * @(a *^ v1) `dot` v2 == a *^ v1 `dot` v2@+class RModule v => InnerProductSpace v where+  dot :: v -> v -> Groundfield v++-- | A normed space is a module with a norm,+--   i.e. a function @norm@ satisfying+--+--   * @norm (a ^* v) = a ^* norm v@+--   * @norm (v1 ^+^ v2) <= norm v1 ^+^ norm v2@+--     (the "triangle inequality")+--+--   A typical example is @sqrt (v `dot` v)@,+--   for an inner product space.+class (Floating (Groundfield v), InnerProductSpace v, VectorSpace v) => NormedSpace v  where+  norm :: v -> Groundfield v+  norm v = sqrt $ v `dot` 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+++-----------------------------+-- 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