packages feed

rhine 0.8.1.1 → 0.9

raw patch · 44 files changed

+1907/−1650 lines, 44 filesdep ~dunaisetup-changed

Dependency ranges changed: dunai

Files

ChangeLog.md view
@@ -1,5 +1,9 @@ # Revision history for rhine +## 0.9++* dunai-0.9 compatibility+ ## 0.8.1.1  * Support for GHC 9.4.4
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
rhine.cabal view
@@ -1,6 +1,6 @@ name:                rhine -version:             0.8.1.1+version:             0.9  synopsis: Functional Reactive Programming with type-level clocks @@ -37,7 +37,7 @@  extra-doc-files:     README.md -cabal-version:       1.18+cabal-version:       2.0  tested-with:   GHC == 8.10.7@@ -52,7 +52,7 @@ source-repository this   type:     git   location: https://github.com/turion/rhine.git-  tag:      v0.8.1.1+  tag:      v0.9  library   exposed-modules:@@ -105,7 +105,7 @@    -- Other library packages from which modules are imported.   build-depends:       base         >= 4.14 && < 4.18-                     , dunai        >= 0.8+                     , dunai        ^>= 0.9                      , transformers >= 0.5                      , time         >= 1.8                      , free         >= 5.1
src/Control/Monad/Schedule.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DeriveFunctor #-}+ {- | This module supplies a general purpose monad transformer that adds a syntactical "delay", or "waiting" side effect.@@ -6,11 +8,8 @@ that implement their waiting actions in 'ScheduleT'. See 'FRP.Rhine.Schedule.Trans' for more details. -}--{-# LANGUAGE DeriveFunctor #-} module Control.Monad.Schedule where - -- base import Control.Concurrent @@ -20,7 +19,6 @@ -- free import Control.Monad.Trans.Free - -- TODO Implement Time via StateT  {- |@@ -30,7 +28,7 @@ * 'a' is the encapsulated value. -} data Wait diff a = Wait diff a-  deriving Functor+  deriving (Functor)  {- | Values in @ScheduleT diff m@ are delayed computations with side effects in 'm'.@@ -39,36 +37,44 @@ -} type ScheduleT diff = FreeT (Wait diff) - -- | The side effect that waits for a specified amount. wait :: Monad m => diff -> ScheduleT diff m () wait diff = FreeT $ return $ Free $ Wait diff $ return () --- | Supply a semantic meaning to 'Wait'.---   For every occurrence of @Wait diff@ in the @ScheduleT diff m a@ value,---   a waiting action is executed, depending on 'diff'.+{- | Supply a semantic meaning to 'Wait'.+   For every occurrence of @Wait diff@ in the @ScheduleT diff m a@ value,+   a waiting action is executed, depending on 'diff'.+-} runScheduleT :: Monad m => (diff -> m ()) -> ScheduleT diff m a -> m a runScheduleT waitAction = iterT $ \(Wait n ma) -> waitAction n >> ma --- | Run a 'ScheduleT' value in a 'MonadIO',---   interpreting the times as milliseconds.-runScheduleIO-  :: (MonadIO m, Integral n)-  => ScheduleT n m a -> m a+{- | Run a 'ScheduleT' value in a 'MonadIO',+   interpreting the times as milliseconds.+-}+runScheduleIO ::+  (MonadIO m, Integral n) =>+  ScheduleT n m a ->+  m a runScheduleIO = runScheduleT $ liftIO . threadDelay . (* 1000) . fromIntegral  -- TODO The definition and type signature are both a mouthful. Is there a simpler concept?--- | Runs two values in 'ScheduleT' concurrently---   and returns the first one that yields a value---   (defaulting to the first argument),---   and a continuation for the other value.-race-  :: (Ord diff, Num diff, Monad m)-  => ScheduleT    diff m a -> ScheduleT diff m b-  -> ScheduleT    diff m (Either-       (                 a,   ScheduleT diff m b)-       (ScheduleT diff m a,                    b)-     )++{- | Runs two values in 'ScheduleT' concurrently+   and returns the first one that yields a value+   (defaulting to the first argument),+   and a continuation for the other value.+-}+race ::+  (Ord diff, Num diff, Monad m) =>+  ScheduleT diff m a ->+  ScheduleT diff m b ->+  ScheduleT+    diff+    m+    ( Either+        (a, ScheduleT diff m b)+        (ScheduleT diff m a, b)+    ) race (FreeT ma) (FreeT mb) = FreeT $ do   -- Perform the side effects to find out how long each 'ScheduleT' values need to wait.   aWait <- ma@@ -78,30 +84,32 @@     Pure a -> return $ Pure $ Left (a, FreeT $ return bWait)     -- 'a' needs to wait, so we need to inspect 'b' as well and see which one needs to wait longer.     Free (Wait aDiff aCont) -> case bWait of-    -- 'b' doesn't need to wait. Return immediately and leave the continuation for 'a'.+      -- 'b' doesn't need to wait. Return immediately and leave the continuation for 'a'.       Pure b -> return $ Pure $ Right (wait aDiff >> aCont, b)       -- Both need to wait. Which one needs to wait longer?-      Free (Wait bDiff bCont) -> if aDiff <= bDiff-        -- 'a' yields first, or both are done simultaneously.-        then runFreeT $ do-          -- Perform the wait action that we've deconstructed-          wait aDiff-          -- Recurse, since more wait actions might be hidden in 'a' and 'b'. 'b' doesn't need to wait as long, since we've already waited for 'aDiff'.-          race aCont $ wait (bDiff - aDiff) >> bCont-        -- 'b' yields first. Analogously.-        else runFreeT $ do-          wait bDiff-          race (wait (aDiff - bDiff) >> aCont) bCont+      Free (Wait bDiff bCont) ->+        if aDiff <= bDiff+          then -- 'a' yields first, or both are done simultaneously.+          runFreeT $ do+            -- Perform the wait action that we've deconstructed+            wait aDiff+            -- Recurse, since more wait actions might be hidden in 'a' and 'b'. 'b' doesn't need to wait as long, since we've already waited for 'aDiff'.+            race aCont $ wait (bDiff - aDiff) >> bCont+          else -- 'b' yields first. Analogously.+          runFreeT $ do+            wait bDiff+            race (wait (aDiff - bDiff) >> aCont) bCont  -- | Runs both schedules concurrently and returns their results at the end.-async-  :: (Ord diff, Num diff, Monad m)-  => ScheduleT diff m  a -> ScheduleT diff m b-  -> ScheduleT diff m (a,                    b)+async ::+  (Ord diff, Num diff, Monad m) =>+  ScheduleT diff m a ->+  ScheduleT diff m b ->+  ScheduleT diff m (a, b) async aSched bSched = do   ab <- race aSched bSched   case ab of-    Left  (a, bCont) -> do+    Left (a, bCont) -> do       b <- bCont       return (a, b)     Right (aCont, b) -> do
src/FRP/Rhine.hs view
@@ -4,52 +4,52 @@ so you will have to import those yourself, e.g. like this:  @-{-# LANGUAGE DataKinds #-} import FRP.Rhine import FRP.Rhine.Clock.Realtime.Millisecond  main :: IO ()-main = flow $ constMCl (putStrLn "Hello World!") @@ (waitClock :: Millisecond 100)+main = flow \$ constMCl (putStrLn \"Hello World!\") \@\@ (waitClock :: Millisecond 100) @ -} module FRP.Rhine (module X) where  -- dunai-import Data.MonadicStreamFunction         as X hiding ((>>>^), (^>>>))-import Data.VectorSpace                   as X+import Data.MonadicStreamFunction as X hiding ((>>>^), (^>>>))+import Data.VectorSpace as X  -- rhine-import FRP.Rhine.Clock                    as X-import FRP.Rhine.Clock.Proxy              as X-import FRP.Rhine.Clock.Util               as X-import FRP.Rhine.ClSF                     as X-import FRP.Rhine.Reactimation             as X++import FRP.Rhine.ClSF as X+import FRP.Rhine.Clock as X+import FRP.Rhine.Clock.Proxy as X+import FRP.Rhine.Clock.Util as X+import FRP.Rhine.Reactimation as X import FRP.Rhine.Reactimation.Combinators as X-import FRP.Rhine.ResamplingBuffer         as X-import FRP.Rhine.ResamplingBuffer.Util    as X-import FRP.Rhine.Schedule                 as X-import FRP.Rhine.SN                       as X-import FRP.Rhine.SN.Combinators           as X-import FRP.Rhine.Type                     as X+import FRP.Rhine.ResamplingBuffer as X+import FRP.Rhine.ResamplingBuffer.Util as X+import FRP.Rhine.SN as X+import FRP.Rhine.SN.Combinators as X+import FRP.Rhine.Schedule as X+import FRP.Rhine.Type as X  -- rhine (components) import FRP.Rhine.Clock.FixedStep as X import FRP.Rhine.Clock.Periodic as X-import FRP.Rhine.Clock.Realtime.Event as X-import FRP.Rhine.Clock.Realtime.Stdin as X import FRP.Rhine.Clock.Realtime.Audio as X import FRP.Rhine.Clock.Realtime.Busy as X+import FRP.Rhine.Clock.Realtime.Event as X import FRP.Rhine.Clock.Realtime.Millisecond as X+import FRP.Rhine.Clock.Realtime.Stdin as X import FRP.Rhine.Clock.Select as X -import FRP.Rhine.ResamplingBuffer.Interpolation as X-import FRP.Rhine.ResamplingBuffer.MSF as X+import FRP.Rhine.ResamplingBuffer.Collect as X import FRP.Rhine.ResamplingBuffer.FIFO as X+import FRP.Rhine.ResamplingBuffer.Interpolation as X+import FRP.Rhine.ResamplingBuffer.KeepLast as X import FRP.Rhine.ResamplingBuffer.LIFO as X-import FRP.Rhine.ResamplingBuffer.Collect as X+import FRP.Rhine.ResamplingBuffer.MSF as X import FRP.Rhine.ResamplingBuffer.Timeless as X-import FRP.Rhine.ResamplingBuffer.KeepLast as X -import FRP.Rhine.Schedule.Trans as X import FRP.Rhine.Schedule.Concurrently as X+import FRP.Rhine.Schedule.Trans as X import FRP.Rhine.Schedule.Util as X
src/FRP/Rhine/ClSF.hs view
@@ -7,13 +7,11 @@ and a wealth of utilities such as digital signal processing units. Documentation can be found in the individual modules. -}--module FRP.Rhine.ClSF ( module X ) where-+module FRP.Rhine.ClSF (module X) where  -- rhine-import FRP.Rhine.ClSF.Core   as X+import FRP.Rhine.ClSF.Core as X import FRP.Rhine.ClSF.Except as X import FRP.Rhine.ClSF.Random as X import FRP.Rhine.ClSF.Reader as X-import FRP.Rhine.ClSF.Util   as X+import FRP.Rhine.ClSF.Util as X
src/FRP/Rhine/ClSF/Core.hs view
@@ -1,19 +1,19 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+ {- | The core functionality of clocked signal functions, supplying the type of clocked signal functions itself ('ClSF'), behaviours (clock-independent/polymorphic signal functions), and basic constructions of 'ClSF's that may use awareness of time as an effect. -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}-module FRP.Rhine.ClSF.Core-  ( module FRP.Rhine.ClSF.Core-  , module Control.Arrow-  , module X-  )-  where+module FRP.Rhine.ClSF.Core (+  module FRP.Rhine.ClSF.Core,+  module Control.Arrow,+  module X,+)+where  -- base import Control.Arrow@@ -26,71 +26,75 @@ import Data.MonadicStreamFunction as X hiding ((>>>^), (^>>>))  -- rhine-import FRP.Rhine.Clock      as X-+import FRP.Rhine.Clock  -- * Clocked signal functions and behaviours --- | A (synchronous, clocked) monadic stream function---   with the additional side effect of being time-aware,---   that is, reading the current 'TimeInfo' of the clock @cl@.+{- | A (synchronous, clocked) monadic stream function+   with the additional side effect of being time-aware,+   that is, reading the current 'TimeInfo' of the clock @cl@.+-} type ClSF m cl a b = MSF (ReaderT (TimeInfo cl) m) a b --- | A clocked signal is a 'ClSF' with no input required.---   It produces its output on its own.-type ClSignal m cl a = forall arbitrary . ClSF m cl arbitrary a+{- | A clocked signal is a 'ClSF' with no input required.+   It produces its output on its own.+-}+type ClSignal m cl a = forall arbitrary. ClSF m cl arbitrary a --- | A (side-effectful) behaviour is a time-aware stream---   that doesn't depend on a particular clock.---   @time@ denotes the 'TimeDomain'.+{- | A (side-effectful) behaviour is a time-aware stream+   that doesn't depend on a particular clock.+   @time@ denotes the 'TimeDomain'.+-} type Behaviour m time a = forall cl. time ~ Time cl => ClSignal m cl a  -- | Compatibility to U.S. american spelling.-type Behavior  m time a = Behaviour m time a+type Behavior m time a = Behaviour m time a --- | A (side-effectful) behaviour function is a time-aware synchronous stream---   function that doesn't depend on a particular clock.---   @time@ denotes the 'TimeDomain'.+{- | A (side-effectful) behaviour function is a time-aware synchronous stream+   function that doesn't depend on a particular clock.+   @time@ denotes the 'TimeDomain'.+-} type BehaviourF m time a b = forall cl. time ~ Time cl => ClSF m cl a b  -- | Compatibility to U.S. american spelling.-type BehaviorF  m time a b = BehaviourF m time a b+type BehaviorF m time a b = BehaviourF m time a b  -- * Utilities to create 'ClSF's from simpler data  -- | Hoist a 'ClSF' along a monad morphism.-hoistClSF-  :: (Monad m1, Monad m2)-  => (forall c. m1 c -> m2 c)-  -> ClSF m1 cl a b-  -> ClSF m2 cl a b+hoistClSF ::+  (Monad m1, Monad m2) =>+  (forall c. m1 c -> m2 c) ->+  ClSF m1 cl a b ->+  ClSF m2 cl a b hoistClSF hoist = morphS $ mapReaderT hoist  -- | Hoist a 'ClSF' and its clock along a monad morphism.-hoistClSFAndClock-  :: (Monad m1, Monad m2)-  => (forall c. m1 c -> m2 c)-  -> ClSF m1 cl a b-  -> ClSF m2 (HoistClock m1 m2 cl) a b-hoistClSFAndClock hoist-  = morphS $ withReaderT (retag id) . mapReaderT hoist+hoistClSFAndClock ::+  (Monad m1, Monad m2) =>+  (forall c. m1 c -> m2 c) ->+  ClSF m1 cl a b ->+  ClSF m2 (HoistClock m1 m2 cl) a b+hoistClSFAndClock hoist =+  morphS $ withReaderT (retag id) . mapReaderT hoist  -- | Lift a 'ClSF' into a monad transformer.-liftClSF-  :: (Monad m, MonadTrans t, Monad (t m))-  => ClSF    m  cl a b-  -> ClSF (t m) cl a b+liftClSF ::+  (Monad m, MonadTrans t, Monad (t m)) =>+  ClSF m cl a b ->+  ClSF (t m) cl a b liftClSF = hoistClSF lift  -- | Lift a 'ClSF' and its clock into a monad transformer.-liftClSFAndClock-  :: (Monad m, MonadTrans t, Monad (t m))-  => ClSF    m                 cl  a b-  -> ClSF (t m) (LiftClock m t cl) a b+liftClSFAndClock ::+  (Monad m, MonadTrans t, Monad (t m)) =>+  ClSF m cl a b ->+  ClSF (t m) (LiftClock m t cl) a b liftClSFAndClock = hoistClSFAndClock lift --- | A monadic stream function without dependency on time---   is a 'ClSF' for any clock.+{- | A monadic stream function without dependency on time+   is a 'ClSF' for any clock.+-} timeless :: Monad m => MSF m a b -> ClSF m cl a b timeless = liftTransS @@ -112,11 +116,12 @@ The former only integrates when the input is @Just 1@, whereas the latter always returns the correct time since initialisation. -}-mapMaybe-  :: Monad m-  => ClSF m cl        a         b-  -> ClSF m cl (Maybe a) (Maybe b)+mapMaybe ::+  Monad m =>+  ClSF m cl a b ->+  ClSF m cl (Maybe a) (Maybe b) mapMaybe behaviour = proc ma -> case ma of-  Nothing -> returnA                -< Nothing-  Just a  -> arr Just <<< behaviour -< a+  Nothing -> returnA -< Nothing+  Just a -> arr Just <<< behaviour -< a+ -- TODO Consider integrating up the time deltas
src/FRP/Rhine/ClSF/Except.hs view
@@ -1,20 +1,23 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+ {- | This module provides exception handling, and thus control flow, to synchronous signal functions.  The API presented here closely follows dunai's 'Control.Monad.Trans.MSF.Except', and reexports everything needed from there. -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}--module FRP.Rhine.ClSF.Except-  ( module FRP.Rhine.ClSF.Except-  , module X-  , safe, safely, exceptS, runMSFExcept, currentInput-  )-  where+module FRP.Rhine.ClSF.Except (+  module FRP.Rhine.ClSF.Except,+  module X,+  safe,+  safely,+  exceptS,+  runMSFExcept,+  currentInput,+)+where  -- base import qualified Control.Category as Category@@ -25,18 +28,19 @@ import Control.Monad.Trans.Reader  -- dunai+import Control.Monad.Trans.MSF.Except hiding (once, once_, throwOn, throwOn', throwS, try) import Data.MonadicStreamFunction-import Control.Monad.Trans.MSF.Except hiding (try, once, once_, throwOn, throwOn', throwS)+ -- TODO Find out whether there is a cleverer way to handle exports import qualified Control.Monad.Trans.MSF.Except as MSFE  -- rhine import FRP.Rhine.ClSF.Core import FRP.Rhine.ClSF.Except.Util+import FRP.Rhine.Clock  -- * Throwing exceptions - -- | Immediately throw the incoming exception. throwS :: Monad m => ClSF (ExceptT e m) cl e a throwS = arrMCl throwE@@ -55,30 +59,33 @@  -- | Variant of 'throwOn', where the exception can vary every tick. throwOn' :: Monad m => ClSF (ExceptT e m) cl (Bool, e) ()-throwOn' = proc (b, e) -> if b-  then throwS  -< e-  else returnA -< ()+throwOn' = proc (b, e) ->+  if b+    then throwS -< e+    else returnA -< ()  -- | Throw the exception 'e' whenever the function evaluates to 'True'. throwOnCond :: Monad m => (a -> Bool) -> e -> ClSF (ExceptT e m) cl a a-throwOnCond cond e = proc a -> if cond a-  then throwS  -< e-  else returnA -< 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'.+{- | Variant of 'throwOnCond' for Kleisli arrows.+   Throws the exception when the input is 'True'.+-} throwOnCondM :: Monad m => (a -> m Bool) -> e -> ClSF (ExceptT e m) cl a a throwOnCondM cond e = proc a -> do   b <- arrMCl (lift . cond) -< a   if b-    then throwS  -< e+    then throwS -< e     else returnA -< a  -- | When the input is @Just e@, throw the exception @e@. throwMaybe :: Monad m => ClSF (ExceptT e m) cl (Maybe e) (Maybe a) throwMaybe = proc me -> case me of   Nothing -> returnA -< Nothing-  Just e  -> throwS  -< e+  Just e -> throwS -< e  -- * Monad interface @@ -101,25 +108,26 @@ or equivalently an exception-throwing behaviour. Any clock with time domain @time@ may occur. -}-type BehaviourFExcept m time a b e-  = forall cl. time ~ Time cl => ClSFExcept m cl a b e+type BehaviourFExcept m time a b e =+  forall cl. time ~ Time cl => ClSFExcept m cl a b e  -- | Compatibility to U.S. american spelling. type BehaviorFExcept m time a b e = BehaviourFExcept m time a b e - -- | Leave the monad context, to use the 'ClSFExcept' as an 'Arrow'. runClSFExcept :: Monad m => ClSFExcept m cl a b e -> ClSF (ExceptT e m) cl a b runClSFExcept = morphS commuteExceptReader . runMSFExcept --- | Enter the monad context in the exception---   for 'ClSF's in the 'ExceptT' monad.---   The 'ClSF' will be run until it encounters an exception.+{- | Enter the monad context in the exception+   for 'ClSF's in the 'ExceptT' monad.+   The 'ClSF' will be run until it encounters an exception.+-} try :: Monad m => ClSF (ExceptT e m) cl a b -> ClSFExcept m cl a b e try = MSFE.try . morphS commuteReaderExcept --- | Within the same tick, perform a monadic action,---   and immediately throw the value as an exception.+{- | Within the same tick, perform a monadic action,+   and immediately throw the value as an exception.+-} once :: Monad m => (a -> m e) -> ClSFExcept m cl a b e once f = MSFE.once $ lift . f @@ -127,8 +135,8 @@ once_ :: Monad m => m e -> ClSFExcept m cl a b e once_ = once . const ---- | Advances a single tick with the given Kleisli arrow,---   and then throws an exception.+{- | Advances a single tick with the given Kleisli arrow,+   and then throws an exception.+-} step :: Monad m => (a -> m (b, e)) -> ClSFExcept m cl a b e step f = MSFE.step $ lift . f
src/FRP/Rhine/ClSF/Except/Util.hs view
@@ -1,7 +1,6 @@-{-|+{- | Utilities for 'FRP.Rhine.ClSF.Except' that need not be exported. -}- module FRP.Rhine.ClSF.Except.Util where  -- transformers
src/FRP/Rhine/ClSF/Random.hs view
@@ -1,16 +1,16 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeFamilies #-}--- | Create 'ClSF's with randomness without 'IO'.---   Uses the @MonadRandom@ package.---   This module copies the API from @dunai@'s---   'Control.Monad.Trans.MSF.Random'. -module FRP.Rhine.ClSF.Random-  ( module FRP.Rhine.ClSF.Random-  , module X-  )-  where-+{- | Create 'ClSF's with randomness without 'IO'.+   Uses the @MonadRandom@ package.+   This module copies the API from @dunai@'s+   'Control.Monad.Trans.MSF.Random'.+-}+module FRP.Rhine.ClSF.Random (+  module FRP.Rhine.ClSF.Random,+  module X,+)+where  -- transformers import Control.Monad.IO.Class@@ -20,8 +20,8 @@  -- dunai import Control.Monad.Trans.MSF.Except (performOnFirstSample)+import Control.Monad.Trans.MSF.Random as X hiding (evalRandS, getRandomRS, getRandomRS_, getRandomS, runRandS) import qualified Control.Monad.Trans.MSF.Random as MSF-import Control.Monad.Trans.MSF.Random as X hiding (runRandS, evalRandS, getRandomS, getRandomRS, getRandomRS_)  -- rhine import FRP.Rhine.ClSF.Core@@ -30,65 +30,67 @@ -- * Generating random values from the 'RandT' transformer  -- | Generates random values, updating the generator on every step.-runRandS-  :: (RandomGen g, Monad m)-  => ClSF (RandT g m) cl a     b-  -> g -- ^ The initial random seed-  -> ClSF          m  cl a (g, b)-runRandS clsf g = MSF.runRandS (morphS commuteReaderRand clsf) g+runRandS ::+  (RandomGen g, Monad m) =>+  ClSF (RandT g m) cl a b ->+  -- | The initial random seed+  g ->+  ClSF m cl a (g, b)+runRandS clsf = MSF.runRandS (morphS commuteReaderRand clsf)  -- | Updates the generator every step but discards the generator.-evalRandS-  :: (RandomGen g, Monad m)-  => ClSF (RandT g m) cl a b-  -> g-  -> ClSF          m  cl a b+evalRandS ::+  (RandomGen g, Monad m) =>+  ClSF (RandT g m) cl a b ->+  g ->+  ClSF m cl a b evalRandS clsf g = runRandS clsf g >>> arr snd --- | Updates the generator every step but discards the value,---   only outputting the generator.-execRandS-  :: (RandomGen g, Monad m)-  => ClSF (RandT g m) cl a b-  -> g-  -> ClSF          m  cl a g+{- | Updates the generator every step but discards the value,+   only outputting the generator.+-}+execRandS ::+  (RandomGen g, Monad m) =>+  ClSF (RandT g m) cl a b ->+  g ->+  ClSF m cl a g execRandS clsf g = runRandS clsf g >>> arr fst  -- | Evaluates the random computation by using the global random generator.-evalRandIOS-  :: Monad m-  =>     ClSF (RandT StdGen m) cl a b-  -> IO (ClSF               m  cl a b)-evalRandIOS clsf = do-  g <- newStdGen-  return $ evalRandS clsf g+evalRandIOS ::+  Monad m =>+  ClSF (RandT StdGen m) cl a b ->+  IO (ClSF m cl a b)+evalRandIOS clsf = evalRandS clsf <$> newStdGen  -- | Evaluates the random computation by using the global random generator on the first tick.-evalRandIOS'-  :: MonadIO m-  => ClSF (RandT StdGen m) cl a b-  -> ClSF               m  cl a b+evalRandIOS' ::+  MonadIO m =>+  ClSF (RandT StdGen m) cl a b ->+  ClSF m cl a b evalRandIOS' = performOnFirstSample . liftIO . evalRandIOS  -- * Creating random behaviours  -- | Produce a random value at every tick.-getRandomS-  :: (MonadRandom m, Random a)-  => Behaviour m time a+getRandomS ::+  (MonadRandom m, Random a) =>+  Behaviour m time a getRandomS = constMCl getRandom --- | Produce a random value at every tick,---   within a range given per tick.-getRandomRS-  :: (MonadRandom m, Random a)-  => BehaviourF m time (a, a) a+{- | Produce a random value at every tick,+   within a range given per tick.+-}+getRandomRS ::+  (MonadRandom m, Random a) =>+  BehaviourF m time (a, a) a getRandomRS = arrMCl getRandomR --- | Produce a random value at every tick,---   within a range given once.-getRandomRS_-  :: (MonadRandom m, Random a)-  => (a, a)-  -> Behaviour m time a+{- | Produce a random value at every tick,+   within a range given once.+-}+getRandomRS_ ::+  (MonadRandom m, Random a) =>+  (a, a) ->+  Behaviour m time a getRandomRS_ range = constMCl $ getRandomR range
src/FRP/Rhine/ClSF/Random/Util.hs view
@@ -1,6 +1,5 @@ module FRP.Rhine.ClSF.Random.Util where - -- transformers import Control.Monad.Trans.Reader @@ -10,4 +9,3 @@ -- | Commute one 'ReaderT' layer past a 'RandT' layer. commuteReaderRand :: ReaderT r (RandT g m) a -> RandT g (ReaderT r m) a commuteReaderRand (ReaderT f) = liftRandT $ \g -> ReaderT $ \r -> runRandT (f r) g-
src/FRP/Rhine/ClSF/Reader.hs view
@@ -1,10 +1,10 @@-{- |-Create and remove 'ReaderT' layers in 'ClSF's.--}- {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-}++{- |+Create and remove 'ReaderT' layers in 'ClSF's.+-} module FRP.Rhine.ClSF.Reader where  -- base@@ -19,31 +19,36 @@ -- rhine import FRP.Rhine.ClSF.Core - -- | Commute two 'ReaderT' transformer layers past each other commuteReaders :: ReaderT r1 (ReaderT r2 m) a -> ReaderT r2 (ReaderT r1 m) a-commuteReaders a-  = ReaderT $ \r1 -> ReaderT $ \r2 -> runReaderT (runReaderT a r2) r1+commuteReaders a =+  ReaderT $ \r1 -> ReaderT $ \r2 -> runReaderT (runReaderT a r2) r1 --- | Create ("wrap") a 'ReaderT' layer in the monad stack of a behaviour.---   Each tick, the 'ReaderT' side effect is performed---   by passing the original behaviour the extra @r@ input.-readerS-  :: Monad m-  => ClSF m cl (a, r) b -> ClSF (ReaderT r m) cl a b-readerS behaviour-  = morphS commuteReaders $ MSF.readerS $ arr swap >>> behaviour+{- | Create ("wrap") a 'ReaderT' layer in the monad stack of a behaviour.+   Each tick, the 'ReaderT' side effect is performed+   by passing the original behaviour the extra @r@ input.+-}+readerS ::+  Monad m =>+  ClSF m cl (a, r) b ->+  ClSF (ReaderT r m) cl a b+readerS behaviour =+  morphS commuteReaders $ MSF.readerS $ arr swap >>> behaviour --- | Remove ("run") a 'ReaderT' layer from the monad stack---   by making it an explicit input to the behaviour.-runReaderS-  :: Monad m-  => ClSF (ReaderT r m) cl a b -> ClSF m cl (a, r) b-runReaderS behaviour-  = arr swap >>> (MSF.runReaderS $ morphS commuteReaders behaviour)+{- | Remove ("run") a 'ReaderT' layer from the monad stack+   by making it an explicit input to the behaviour.+-}+runReaderS ::+  Monad m =>+  ClSF (ReaderT r m) cl a b ->+  ClSF m cl (a, r) b+runReaderS behaviour =+  arr swap >>> MSF.runReaderS (morphS commuteReaders behaviour)  -- | Remove a 'ReaderT' layer by passing the readonly environment explicitly.-runReaderS_-  :: Monad m-  => ClSF (ReaderT r m) cl a b -> r -> ClSF m cl a b-runReaderS_ behaviour r = arr (, r) >>> runReaderS behaviour+runReaderS_ ::+  Monad m =>+  ClSF (ReaderT r m) cl a b ->+  r ->+  ClSF m cl a b+runReaderS_ behaviour r = arr (,r) >>> runReaderS behaviour
src/FRP/Rhine/ClSF/Upsample.hs view
@@ -1,9 +1,9 @@--- | Utilities to run 'ClSF's at the speed of combined clocks---   when they are defined only for a constituent clock.- {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} +{- | Utilities to run 'ClSF's at the speed of combined clocks+   when they are defined only for a constituent clock.+-} module FRP.Rhine.ClSF.Upsample where  -- dunai@@ -11,41 +11,48 @@  -- rhine import FRP.Rhine.ClSF.Core+import FRP.Rhine.Clock import FRP.Rhine.Schedule --- | An 'MSF' can be given arbitrary other arguments---   that cause it to tick without doing anything---   and replicating the last output.+{- | An 'MSF' can be given arbitrary other arguments+   that cause it to tick without doing anything+   and replicating the last output.+-} upsampleMSF :: Monad m => b -> MSF m a b -> MSF m (Either arbitrary a) b upsampleMSF b msf = right msf >>> accumulateWith (<>) (Right b) >>> arr fromRight   where     fromRight (Right b') = b'-    fromRight (Left  _ ) = error "fromRight: This case never occurs in upsampleMSF."+    fromRight (Left _) = error "fromRight: This case never occurs in upsampleMSF."+ -- Note that the Semigroup instance of Either a arbitrary -- updates when the first argument is Right. ---- | Upsample a 'ClSF' to a parallel clock.---   The given 'ClSF' is only called when @clR@ ticks,---   otherwise the last output is replicated---   (with the given @b@ as initialisation).-upsampleR-  :: (Monad m, Time clL ~ Time clR)-  => b -> ClSF m clR a b -> ClSF m (ParallelClock m clL clR) a b+{- | Upsample a 'ClSF' to a parallel clock.+   The given 'ClSF' is only called when @clR@ ticks,+   otherwise the last output is replicated+   (with the given @b@ as initialisation).+-}+upsampleR ::+  (Monad m, Time clL ~ Time clR) =>+  b ->+  ClSF m clR a b ->+  ClSF m (ParallelClock m clL clR) a b upsampleR b clsf = readerS $ arr remap >>> upsampleMSF b (runReaderS clsf)   where-    remap (TimeInfo { tag = Left  tag     }, _) = Left tag-    remap (TimeInfo { tag = Right tag, .. }, a) = Right (TimeInfo { .. }, a)-+    remap (TimeInfo {tag = Left tag}, _) = Left tag+    remap (TimeInfo {tag = Right tag, ..}, a) = Right (TimeInfo {..}, a) --- | Upsample a 'ClSF' to a parallel clock.---   The given 'ClSF' is only called when @clL@ ticks,---   otherwise the last output is replicated---   (with the given @b@ as initialisation).-upsampleL-  :: (Monad m, Time clL ~ Time clR)-  => b -> ClSF m clL a b -> ClSF m (ParallelClock m clL clR) a b+{- | Upsample a 'ClSF' to a parallel clock.+   The given 'ClSF' is only called when @clL@ ticks,+   otherwise the last output is replicated+   (with the given @b@ as initialisation).+-}+upsampleL ::+  (Monad m, Time clL ~ Time clR) =>+  b ->+  ClSF m clL a b ->+  ClSF m (ParallelClock m clL clR) a b upsampleL b clsf = readerS $ arr remap >>> upsampleMSF b (runReaderS clsf)   where-    remap (TimeInfo { tag = Right tag     }, _) = Left tag-    remap (TimeInfo { tag = Left  tag, .. }, a) = Right (TimeInfo { .. }, a)+    remap (TimeInfo {tag = Right tag}, _) = Left tag+    remap (TimeInfo {tag = Left tag, ..}, a) = Right (TimeInfo {..}, a)
src/FRP/Rhine/ClSF/Util.hs view
@@ -1,19 +1,17 @@-{- |-Utilities to create 'ClSF's.-The fundamental effect that 'ClSF's have is-reading the time information of the clock.-It can be used for many purposes, for example digital signal processing.--}- {-# LANGUAGE Arrows #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} +{- |+Utilities to create 'ClSF's.+The fundamental effect that 'ClSF's have is+reading the time information of the clock.+It can be used for many purposes, for example digital signal processing.+-} module FRP.Rhine.ClSF.Util where - -- base import Control.Arrow import Control.Category (Category)@@ -37,7 +35,7 @@ -- rhine import FRP.Rhine.ClSF.Core import FRP.Rhine.ClSF.Except-+import FRP.Rhine.Clock  -- * Read time information @@ -91,14 +89,15 @@ since it doesn't reset after restarting the sawtooth. -} sinceStart :: (Monad m, TimeDomain time) => BehaviourF m time a (Diff time)-sinceStart = absoluteS >>> proc time -> do-  startTime <- keepFirst -< time-  returnA                -< time `diffTime` startTime-+sinceStart =+  absoluteS >>> proc time -> do+    startTime <- keepFirst -< time+    returnA -< time `diffTime` startTime  -- * Useful aliases  -- TODO Is it cleverer to generalise to Arrow?+ {- | Alias for 'Control.Category.>>>' (sequential composition) with higher operator precedence, designed to work with the other operators, e.g.: @@ -109,18 +108,22 @@ > (>->) :: Monad m => ClSF m cl a b -> ClSF m cl b c -> ClSF m cl a c -} infixr 6 >->-(>->) :: Category cat-      => cat a b-      -> cat   b c-      -> cat a   c++(>->) ::+  Category cat =>+  cat a b ->+  cat b c ->+  cat a c (>->) = (>>>)  -- | Alias for 'Control.Category.<<<'. infixl 6 <-<-(<-<) :: Category cat-      => cat   b c-      -> cat a b-      -> cat a   c++(<-<) ::+  Category cat =>+  cat b c ->+  cat a b ->+  cat a c (<-<) = (<<<)  {- | Output a constant value.@@ -131,183 +134,227 @@ arr_ :: Arrow a => b -> a c b arr_ = arr . const - -- | The identity synchronous stream function. clId :: Monad m => ClSF m cl a a clId = Control.Category.id - -- * Basic signal processing components  -- ** Integration and differentiation --- | The output of @integralFrom v0@ is the numerical Euler integral---   of the input, with initial offset @v0@.-integralFrom-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => v -> BehaviorF m td v v+{- | The output of @integralFrom v0@ is the numerical Euler integral+   of the input, with initial offset @v0@.+-}+integralFrom ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  v ->+  BehaviorF m td v v integralFrom v0 = proc v -> do   _sinceLast <- timeInfoOf sinceLast -< ()-  sumFrom v0                         -< _sinceLast *^ v+  sumFrom v0 -< _sinceLast *^ v  -- | Euler integration, with zero initial offset.-integral-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => BehaviorF m td v v+integral ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  BehaviorF m td v v integral = integralFrom zeroVector ---- | The output of @derivativeFrom v0@ is the numerical derivative of the input,---   with a Newton difference quotient.---   The input is initialised with @v0@.-derivativeFrom-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => v -> BehaviorF m td v v+{- | The output of @derivativeFrom v0@ is the numerical derivative of the input,+   with a Newton difference quotient.+   The input is initialised with @v0@.+-}+derivativeFrom ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  v ->+  BehaviorF m td v v derivativeFrom v0 = proc v -> do-  vLast         <- iPre v0  -< v+  vLast <- iPre v0 -< v   TimeInfo {..} <- timeInfo -< ()-  returnA                   -< (v ^-^ vLast) ^/ sinceLast+  returnA -< (v ^-^ vLast) ^/ sinceLast  -- | Numerical derivative with input initialised to zero.-derivative-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => BehaviorF m td v v+derivative ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  BehaviorF m td v v derivative = derivativeFrom zeroVector --- | Like 'derivativeFrom', but uses three samples to compute the derivative.---   Consequently, it is delayed by one sample.-threePointDerivativeFrom-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => v -- ^ The initial position-  -> BehaviorF m td v v+{- | Like 'derivativeFrom', but uses three samples to compute the derivative.+   Consequently, it is delayed by one sample.+-}+threePointDerivativeFrom ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  -- | The initial position+  v ->+  BehaviorF m td v v threePointDerivativeFrom v0 = proc v -> do-  dv  <- derivativeFrom v0 -< v-  dv' <- iPre zeroVector   -< dv-  returnA                  -< (dv ^+^ dv') ^/ 2+  dv <- derivativeFrom v0 -< v+  dv' <- iPre zeroVector -< dv+  returnA -< (dv ^+^ dv') ^/ 2 --- | Like 'threePointDerivativeFrom',---   but with the initial position initialised to 'zeroVector'.-threePointDerivative-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => BehaviorF m td v v+{- | Like 'threePointDerivativeFrom',+   but with the initial position initialised to 'zeroVector'.+-}+threePointDerivative ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  BehaviorF m td v v threePointDerivative = threePointDerivativeFrom zeroVector  -- ** Averaging and filters --- | A weighted moving average signal function.---   The output is the average of the first input,---   weighted by the second input---   (which is assumed to be always between 0 and 1).---   The weight is applied to the average of the last tick,---   so a weight of 1 simply repeats the past value unchanged,---   whereas a weight of 0 outputs the current value.-weightedAverageFrom-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => v -- ^ The initial position-  -> BehaviorF m td (v, s) v+{- | A weighted moving average signal function.+   The output is the average of the first input,+   weighted by the second input+   (which is assumed to be always between 0 and 1).+   The weight is applied to the average of the last tick,+   so a weight of 1 simply repeats the past value unchanged,+   whereas a weight of 0 outputs the current value.+-}+weightedAverageFrom ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  -- | The initial position+  v ->+  BehaviorF m td (v, s) v weightedAverageFrom v0 = feedback v0 $ proc ((v, weight), vAvg) -> do   let     vAvg' = weight *^ vAvg ^+^ (1 - weight) *^ v   returnA -< (vAvg', vAvg') --- | An exponential moving average, or low pass.---   It will average out, or filter,---   all features below a given time constant @t@.---   (Equivalently, it filters out frequencies above @1 / (2 * pi * t)@.)-averageFrom-  :: ( Monad m, VectorSpace v s-     , Floating s-     , s ~ Diff td)-  => v -- ^ The initial position-  -> Diff td -- ^ The time scale on which the signal is averaged-  -> BehaviorF m td v v+{- | An exponential moving average, or low pass.+   It will average out, or filter,+   all features below a given time constant @t@.+   (Equivalently, it filters out frequencies above @1 / (2 * pi * t)@.)+-}+averageFrom ::+  ( Monad m+  , VectorSpace v s+  , Floating s+  , s ~ Diff td+  ) =>+  -- | The initial position+  v ->+  -- | The time scale on which the signal is averaged+  Diff td ->+  BehaviorF m td v v averageFrom v0 t = proc v -> do   TimeInfo {..} <- timeInfo -< ()   let-    weight = exp $ - (sinceLast / t)-  weightedAverageFrom v0    -< (v, weight)-+    weight = exp $ -(sinceLast / t)+  weightedAverageFrom v0 -< (v, weight)  -- | An average, or low pass, initialised to zero.-average-  :: ( Monad m, VectorSpace v s-     , Floating s-     , s ~ Diff td)-  => Diff td -- ^ The time scale on which the signal is averaged-  -> BehaviourF m td v v+average ::+  ( Monad m+  , VectorSpace v s+  , Floating s+  , s ~ Diff td+  ) =>+  -- | The time scale on which the signal is averaged+  Diff td ->+  BehaviourF m td v v average = averageFrom zeroVector --- | A linearised version of 'averageFrom'.---   It is more efficient, but only accurate---   if the supplied time scale is much bigger---   than the average time difference between two ticks.-averageLinFrom-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => v -- ^ The initial position-  -> Diff td -- ^ The time scale on which the signal is averaged-  -> BehaviourF m td v v+{- | A linearised version of 'averageFrom'.+   It is more efficient, but only accurate+   if the supplied time scale is much bigger+   than the average time difference between two ticks.+-}+averageLinFrom ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  -- | The initial position+  v ->+  -- | The time scale on which the signal is averaged+  Diff td ->+  BehaviourF m td v v averageLinFrom v0 t = proc v -> do   TimeInfo {..} <- timeInfo -< ()   let     weight = t / (sinceLast + t)-  weightedAverageFrom v0    -< (v, weight)+  weightedAverageFrom v0 -< (v, weight)  -- | Linearised version of 'average'.-averageLin-  :: ( Monad m, VectorSpace v s-     , s ~ Diff td)-  => Diff td -- ^ The time scale on which the signal is averaged-  -> BehaviourF m td v v+averageLin ::+  ( Monad m+  , VectorSpace v s+  , s ~ Diff td+  ) =>+  -- | The time scale on which the signal is averaged+  Diff td ->+  BehaviourF m td v v averageLin = averageLinFrom zeroVector  -- *** First-order filters  -- | Alias for 'average'.-lowPass-  :: ( Monad m, VectorSpace v s-     , Floating s-     , s ~ Diff td)-  => Diff td-  -> BehaviourF m td v v+lowPass ::+  ( Monad m+  , VectorSpace v s+  , Floating s+  , s ~ Diff td+  ) =>+  Diff td ->+  BehaviourF m td v v lowPass = average  -- | Filters out frequencies below @1 / (2 * pi * t)@.-highPass-  :: ( Monad m, VectorSpace v s-     , Floating s-     , s ~ Diff td)-  => Diff td -- ^ The time constant @t@-  -> BehaviourF m td v v+highPass ::+  ( Monad m+  , VectorSpace v s+  , Floating s+  , s ~ Diff td+  ) =>+  -- | The time constant @t@+  Diff td ->+  BehaviourF m td v v highPass t = clId ^-^ lowPass t  -- | Filters out frequencies other than @1 / (2 * pi * t)@.-bandPass-  :: ( Monad m, VectorSpace v s-     , Floating s-     , s ~ Diff td)-  => Diff td -- ^ The time constant @t@-  -> BehaviourF m td v v+bandPass ::+  ( Monad m+  , VectorSpace v s+  , Floating s+  , s ~ Diff td+  ) =>+  -- | The time constant @t@+  Diff td ->+  BehaviourF m td v v bandPass t = lowPass t >>> highPass t  -- | Filters out the frequency @1 / (2 * pi * t)@.-bandStop-  :: ( Monad m, VectorSpace v s-     , Floating s-     , s ~ Diff td)-  => Diff td -- ^ The time constant @t@-  -> BehaviourF m td v v+bandStop ::+  ( Monad m+  , VectorSpace v s+  , Floating s+  , s ~ Diff td+  ) =>+  -- | The time constant @t@+  Diff td ->+  BehaviourF m td v v bandStop t = clId ^-^ bandPass t -- -- * Delays  -- | Remembers and indefinitely outputs ("holds") the first input value.@@ -316,69 +363,74 @@   a <- try throwS   safe $ arr $ const a --- | Remembers all input values that arrived within a given time window.---   New values are appended left.-historySince-  :: (Monad m, Ord (Diff (Time cl)), TimeDomain (Time cl))-  => Diff (Time cl) -- ^ The size of the time window-  -> ClSF m cl a (Seq (TimeInfo cl, a))+{- | Remembers all input values that arrived within a given time window.+   New values are appended left.+-}+historySince ::+  (Monad m, Ord (Diff (Time cl)), TimeDomain (Time cl)) =>+  -- | The size of the time window+  Diff (Time cl) ->+  ClSF m cl a (Seq (TimeInfo cl, a)) historySince dTime = readerS $ accumulateWith appendValue empty   where-    appendValue (ti, a) tias  = takeWhileL (recentlySince ti) $ (ti, a) <| tias+    appendValue (ti, a) tias = takeWhileL (recentlySince ti) $ (ti, a) <| tias     recentlySince ti (ti', _) = diffTime (absolute ti) (absolute ti') < dTime --- | Delay a signal by certain time span,---   initialising with the first input.-delayBy-  :: (Monad m, Ord (Diff td), TimeDomain td)-  => Diff td            -- ^ The time span to delay the signal-  -> BehaviorF m td a a+{- | Delay a signal by certain time span,+   initialising with the first input.+-}+delayBy ::+  (Monad m, Ord (Diff td), TimeDomain td) =>+  -- | The time span to delay the signal+  Diff td ->+  BehaviorF m td a a delayBy dTime = historySince dTime >>> arr (viewr >>> safeHead) >>> lastS undefined >>> arr snd   where-    safeHead EmptyR   = Nothing+    safeHead EmptyR = Nothing     safeHead (_ :> a) = Just a  -- * Timers --- | Throws an exception after the specified time difference,---   outputting the time passed since the 'timer' was instantiated.-timer-  :: ( Monad m-     , TimeDomain td-     , Ord (Diff td)-     )-  => Diff td-  -> BehaviorF (ExceptT () m) td a (Diff td)+{- | Throws an exception after the specified time difference,+   outputting the time passed since the 'timer' was instantiated.+-}+timer ::+  ( Monad m+  , TimeDomain td+  , Ord (Diff td)+  ) =>+  Diff td ->+  BehaviorF (ExceptT () m) td a (Diff td) timer diff = proc _ -> do   time <- sinceStart -< ()-  _    <- throwOn () -< time > diff-  returnA            -< time+  _ <- throwOn () -< time > diff+  returnA -< time  -- | Like 'timer_', but doesn't output the remaining time at all.-timer_-  :: ( Monad m-     , TimeDomain td-     , Ord (Diff td)-     )-  => Diff td-  -> BehaviorF (ExceptT () m) td a ()+timer_ ::+  ( Monad m+  , TimeDomain td+  , Ord (Diff td)+  ) =>+  Diff td ->+  BehaviorF (ExceptT () m) td a () timer_ diff = timer diff >>> arr (const ())  -- | Like 'timer', but divides the remaining time by the total time.-scaledTimer-  :: ( Monad m-     , TimeDomain td-     , Fractional (Diff td)-     , Ord        (Diff td)-     )-  => Diff td-  -> BehaviorF (ExceptT () m) td a (Diff td)+scaledTimer ::+  ( Monad m+  , TimeDomain td+  , Fractional (Diff td)+  , Ord (Diff td)+  ) =>+  Diff td ->+  BehaviorF (ExceptT () m) td a (Diff td) scaledTimer diff = timer diff >>> arr (/ diff) - -- * To be ported to Dunai --- | Remembers the last 'Just' value,---   defaulting to the given initialisation value.+{- | Remembers the last 'Just' value,+   defaulting to the given initialisation value.+-} lastS :: Monad m => a -> MSF m (Maybe a) a lastS a = arr Last >>> mappendFrom (Last (Just a)) >>> arr (getLast >>> fromJust)
src/FRP/Rhine/Clock.hs view
@@ -1,3 +1,11 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+ {- | 'Clock's are the central new notion in Rhine. There are clock types (instances of the 'Clock' type class)@@ -7,25 +15,18 @@ and certain general constructions of 'Clock's, such as clocks lifted along monad morphisms or time rescalings. -}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-}-module FRP.Rhine.Clock-  ( module FRP.Rhine.Clock-  , module X-  )+module FRP.Rhine.Clock (+  module FRP.Rhine.Clock,+  module X,+) where  -- base import qualified Control.Category as Category  -- transformers-import Control.Monad.IO.Class (liftIO, MonadIO)-import Control.Monad.Trans.Class (lift, MonadTrans)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.Trans.Class (MonadTrans, lift)  -- dunai import Data.MonadicStreamFunction as X hiding ((>>>^), (^>>>))@@ -59,36 +60,41 @@ class TimeDomain (Time cl) => Clock m cl where   -- | The time domain, i.e. type of the time stamps the clock creates.   type Time cl+   -- | Additional information that the clock may output at each tick,   --   e.g. if a realtime promise was met, if an event occurred,   --   if one of its subclocks (if any) ticked.   type Tag cl+   -- | The method that produces to a clock value a running clock,   --   i.e. an effectful stream of tagged time stamps together with an initialisation time.-  initClock-    :: cl -- ^ The clock value, containing e.g. settings or device parameters-    -> RunningClockInit m (Time cl) (Tag cl) -- ^ The stream of time stamps, and the initial time+  initClock ::+    -- | The clock value, containing e.g. settings or device parameters+    cl ->+    -- | The stream of time stamps, and the initial time+    RunningClockInit m (Time cl) (Tag cl)  -- * Auxiliary definitions and utilities  -- | An annotated, rich time stamp. data TimeInfo cl = TimeInfo-  { -- | Time passed since the last tick-    sinceLast :: Diff (Time cl)-    -- | Time passed since the initialisation of the clock+  { sinceLast :: Diff (Time cl)+  -- ^ Time passed since the last tick   , sinceInit :: Diff (Time cl)-    -- | The absolute time of the current tick-  , absolute  :: Time cl-    -- | The tag annotation of the current tick-  , tag       :: Tag cl+  -- ^ Time passed since the initialisation of the clock+  , absolute :: Time cl+  -- ^ The absolute time of the current tick+  , tag :: Tag cl+  -- ^ The tag annotation of the current tick   }  -- | A utility that changes the tag of a 'TimeInfo'.-retag-  :: (Time cl1 ~ Time cl2)-  => (Tag cl1 -> Tag cl2)-  -> TimeInfo cl1 -> TimeInfo cl2-retag f TimeInfo {..} = TimeInfo { tag = f tag, .. }+retag ::+  (Time cl1 ~ Time cl2) =>+  (Tag cl1 -> Tag cl2) ->+  TimeInfo cl1 ->+  TimeInfo cl2+retag f TimeInfo {..} = TimeInfo {tag = f tag, ..}  -- * Certain universal building blocks to produce new clocks from given ones @@ -97,41 +103,48 @@ -- | A pure morphism of time domains is just a function. type Rescaling cl time = Time cl -> time --- | An effectful morphism of time domains is a Kleisli arrow.---   It can use a side effect to rescale a point in one time domain---   into another one.+{- | An effectful morphism of time domains is a Kleisli arrow.+   It can use a side effect to rescale a point in one time domain+   into another one.+-} type RescalingM m cl time = Time cl -> m time --- | An effectful, stateful morphism of time domains is an 'MSF'---   that uses side effects to rescale a point in one time domain---   into another one.+{- | An effectful, stateful morphism of time domains is an 'MSF'+   that uses side effects to rescale a point in one time domain+   into another one.+-} type RescalingS m cl time tag = MSF m (Time cl, Tag cl) (time, tag) --- | Like 'RescalingS', but allows for an initialisation---   of the rescaling morphism, together with the initial time.+{- | Like 'RescalingS', but allows for an initialisation+   of the rescaling morphism, together with the initial time.+-} type RescalingSInit m cl time tag = Time cl -> m (RescalingS m cl time tag, time) --- | Convert an effectful morphism of time domains into a stateful one with initialisation.---   Think of its type as @RescalingM m cl time -> RescalingSInit m cl time tag@,---   although this type is ambiguous.-rescaleMToSInit-  :: Monad m-  => (time1 -> m time2) -> time1 -> m (MSF m (time1, tag) (time2, tag), time2)-rescaleMToSInit rescaling time1 = (arrM rescaling *** Category.id, ) <$> rescaling time1+{- | Convert an effectful morphism of time domains into a stateful one with initialisation.+   Think of its type as @RescalingM m cl time -> RescalingSInit m cl time tag@,+   although this type is ambiguous.+-}+rescaleMToSInit ::+  Monad m =>+  (time1 -> m time2) ->+  time1 ->+  m (MSF m (time1, tag) (time2, tag), time2)+rescaleMToSInit rescaling time1 = (arrM rescaling *** Category.id,) <$> rescaling time1  -- ** Applying rescalings to clocks  -- | Applying a morphism of time domains yields a new clock. data RescaledClock cl time = RescaledClock   { unscaledClock :: cl-  , rescale       :: Rescaling cl time+  , rescale :: Rescaling cl time   } --instance (Monad m, TimeDomain time, Clock m cl)-      => Clock m (RescaledClock cl time) where+instance+  (Monad m, TimeDomain time, Clock m cl) =>+  Clock m (RescaledClock cl time)+  where   type Time (RescaledClock cl time) = time-  type Tag  (RescaledClock cl time) = Tag cl+  type Tag (RescaledClock cl time) = Tag cl   initClock (RescaledClock cl f) = do     (runningClock, initTime) <- initClock cl     return@@ -139,22 +152,25 @@       , f initTime       ) --- | Instead of a mere function as morphism of time domains,---   we can transform one time domain into the other with an effectful morphism.+{- | Instead of a mere function as morphism of time domains,+   we can transform one time domain into the other with an effectful morphism.+-} data RescaledClockM m cl time = RescaledClockM   { unscaledClockM :: cl   -- ^ The clock before the rescaling-  , rescaleM       :: RescalingM m cl time+  , rescaleM :: RescalingM m cl time   -- ^ Computing the new time effectfully from the old time   } -instance (Monad m, TimeDomain time, Clock m cl)-      => Clock m (RescaledClockM m cl time) where+instance+  (Monad m, TimeDomain time, Clock m cl) =>+  Clock m (RescaledClockM m cl time)+  where   type Time (RescaledClockM m cl time) = time-  type Tag  (RescaledClockM m cl time) = Tag cl+  type Tag (RescaledClockM m cl time) = Tag cl   initClock RescaledClockM {..} = do     (runningClock, initTime) <- initClock unscaledClockM-    rescaledInitTime         <- rescaleM initTime+    rescaledInitTime <- rescaleM initTime     return       ( runningClock >>> first (arrM rescaleM)       , rescaledInitTime@@ -162,26 +178,29 @@  -- | A 'RescaledClock' is trivially a 'RescaledClockM'. rescaledClockToM :: Monad m => RescaledClock cl time -> RescaledClockM m cl time-rescaledClockToM RescaledClock {..} = RescaledClockM-  { unscaledClockM = unscaledClock-  , rescaleM       = return . rescale-  }-+rescaledClockToM RescaledClock {..} =+  RescaledClockM+    { unscaledClockM = unscaledClock+    , rescaleM = return . rescale+    } --- | Instead of a mere function as morphism of time domains,---   we can transform one time domain into the other with a monadic stream function.+{- | Instead of a mere function as morphism of time domains,+   we can transform one time domain into the other with a monadic stream function.+-} data RescaledClockS m cl time tag = RescaledClockS   { unscaledClockS :: cl   -- ^ The clock before the rescaling-  , rescaleS       :: RescalingSInit m cl time tag+  , rescaleS :: RescalingSInit m cl time tag   -- ^ The rescaling stream function, and rescaled initial time,   --   depending on the initial time before rescaling   } -instance (Monad m, TimeDomain time, Clock m cl)-      => Clock m (RescaledClockS m cl time tag) where+instance+  (Monad m, TimeDomain time, Clock m cl) =>+  Clock m (RescaledClockS m cl time tag)+  where   type Time (RescaledClockS m cl time tag) = time-  type Tag  (RescaledClockS m cl time tag) = tag+  type Tag (RescaledClockS m cl time tag) = tag   initClock RescaledClockS {..} = do     (runningClock, initTime) <- initClock unscaledClockS     (rescaling, rescaledInitTime) <- rescaleS initTime@@ -191,30 +210,35 @@       )  -- | A 'RescaledClockM' is trivially a 'RescaledClockS'.-rescaledClockMToS-  :: Monad m-  => RescaledClockM m cl time -> RescaledClockS m cl time (Tag cl)-rescaledClockMToS RescaledClockM {..} = RescaledClockS-  { unscaledClockS = unscaledClockM-  , rescaleS       = rescaleMToSInit rescaleM-  }+rescaledClockMToS ::+  Monad m =>+  RescaledClockM m cl time ->+  RescaledClockS m cl time (Tag cl)+rescaledClockMToS RescaledClockM {..} =+  RescaledClockS+    { unscaledClockS = unscaledClockM+    , rescaleS = rescaleMToSInit rescaleM+    }  -- | A 'RescaledClock' is trivially a 'RescaledClockS'.-rescaledClockToS-  :: Monad m-  => RescaledClock cl time -> RescaledClockS m cl time (Tag cl)+rescaledClockToS ::+  Monad m =>+  RescaledClock cl time ->+  RescaledClockS m cl time (Tag cl) rescaledClockToS = rescaledClockMToS . rescaledClockToM  -- | Applying a monad morphism yields a new clock. data HoistClock m1 m2 cl = HoistClock   { unhoistedClock :: cl-  , monadMorphism  :: forall a . m1 a -> m2 a+  , monadMorphism :: forall a. m1 a -> m2 a   } -instance (Monad m1, Monad m2, Clock m1 cl)-      => Clock m2 (HoistClock m1 m2 cl) where+instance+  (Monad m1, Monad m2, Clock m1 cl) =>+  Clock m2 (HoistClock m1 m2 cl)+  where   type Time (HoistClock m1 m2 cl) = Time cl-  type Tag  (HoistClock m1 m2 cl) = Tag  cl+  type Tag (HoistClock m1 m2 cl) = Tag cl   initClock HoistClock {..} = do     (runningClock, initialTime) <- monadMorphism $ initClock unhoistedClock     let hoistMSF = morphS@@ -224,23 +248,24 @@       , initialTime       ) - -- | Lift a clock type into a monad transformer. type LiftClock m t cl = HoistClock m (t m) cl  -- | Lift a clock value into a monad transformer. liftClock :: (Monad m, MonadTrans t) => cl -> LiftClock m t cl-liftClock unhoistedClock = HoistClock-  { monadMorphism = lift-  , ..-  }+liftClock unhoistedClock =+  HoistClock+    { monadMorphism = lift+    , ..+    }  -- | Lift a clock type into 'MonadIO'. type IOClock m cl = HoistClock IO m cl  -- | Lift a clock value into 'MonadIO'. ioClock :: MonadIO m => cl -> IOClock m cl-ioClock unhoistedClock = HoistClock-  { monadMorphism = liftIO-  , ..-  }+ioClock unhoistedClock =+  HoistClock+    { monadMorphism = liftIO+    , ..+    }
src/FRP/Rhine/Clock/FixedStep.hs view
@@ -1,18 +1,16 @@-{- |-Implements pure clocks ticking at-every multiple of a fixed number of steps,-and a deterministic schedule for such clocks.--}- {-# LANGUAGE Arrows #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-module FRP.Rhine.Clock.FixedStep where +{- |+Implements pure clocks ticking at+every multiple of a fixed number of steps,+and a deterministic schedule for such clocks.+-}+module FRP.Rhine.Clock.FixedStep where  -- base import Data.Maybe (fromMaybe)@@ -32,10 +30,11 @@ import FRP.Rhine.ResamplingBuffer.Util import FRP.Rhine.Schedule --- | A pure (side effect free) clock with fixed step size,---   i.e. ticking at multiples of 'n'.---   The tick rate is in the type signature,---   which prevents composition of signals at different rates.+{- | A pure (side effect free) clock with fixed step size,+   i.e. ticking at multiples of 'n'.+   The tick rate is in the type signature,+   which prevents composition of signals at different rates.+-} data FixedStep (n :: Nat) where   FixedStep :: KnownNat n => FixedStep n -- TODO Does the constraint bring any benefit? @@ -45,12 +44,14 @@  instance Monad m => Clock m (FixedStep n) where   type Time (FixedStep n) = Integer-  type Tag  (FixedStep n) = ()-  initClock cl = return-    ( count >>> arr (* stepsize cl)-      &&& arr (const ())-    , 0-    )+  type Tag (FixedStep n) = ()+  initClock cl =+    return+      ( count+          >>> arr (* stepsize cl)+            &&& arr (const ())+      , 0+      )  instance GetClockProxy (FixedStep n) @@ -58,30 +59,36 @@ type Count = FixedStep 1  -- | Two 'FixedStep' clocks can always be scheduled without side effects.-scheduleFixedStep-  :: Monad m-  => Schedule m (FixedStep n1) (FixedStep n2)-scheduleFixedStep = Schedule f where-  f cl1 cl2 = return (msf, 0)-    where-      n1 = stepsize cl1-      n2 = stepsize cl2-      msf = concatS $ proc _ -> do-        k <- arr (+1) <<< count -< ()-        returnA                 -< [ (k, Left  ()) | k `mod` n1 == 0 ]-                                ++ [ (k, Right ()) | k `mod` n2 == 0 ]+scheduleFixedStep ::+  Monad m =>+  Schedule m (FixedStep n1) (FixedStep n2)+scheduleFixedStep = Schedule f+  where+    f cl1 cl2 = return (msf, 0)+      where+        n1 = stepsize cl1+        n2 = stepsize cl2+        msf = concatS $ proc _ -> do+          k <- arr (+ 1) <<< count -< ()+          returnA+            -<+              [(k, Left ()) | k `mod` n1 == 0]+                ++ [(k, Right ()) | k `mod` n2 == 0]  -- TODO The problem is that the schedule doesn't give a guarantee where in the n ticks of the first clock the second clock will tick. -- For this to work, it has to be the last. -- With scheduleFixedStep, this works, -- but the user might implement an incorrect schedule.-downsampleFixedStep-  :: (KnownNat n, Monad m)-  => ResamplingBuffer m (FixedStep k) (FixedStep (n * k)) a (Vector n a)+downsampleFixedStep ::+  (KnownNat n, Monad m) =>+  ResamplingBuffer m (FixedStep k) (FixedStep (n * k)) a (Vector n a) downsampleFixedStep = collect >>-^ arr (fromList >>> assumeSize)   where-    assumeSize = fromMaybe $ error $ unwords-      [ "You are using an incorrectly implemented schedule"-      , "for two FixedStep clocks."-      , "Use a correct schedule like downsampleFixedStep."-      ]+    assumeSize =+      fromMaybe $+        error $+          unwords+            [ "You are using an incorrectly implemented schedule"+            , "for two FixedStep clocks."+            , "Use a correct schedule like downsampleFixedStep."+            ]
src/FRP/Rhine/Clock/Periodic.hs view
@@ -1,9 +1,3 @@-{- |-Periodic clocks are defined by a stream of ticks with periodic time differences.-They model subclocks of a fixed reference clock.-The time differences are supplied at the type level.--}- {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -12,40 +6,50 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-}++{- |+Periodic clocks are defined by a stream of ticks with periodic time differences.+They model subclocks of a fixed reference clock.+The time differences are supplied at the type level.+-} module FRP.Rhine.Clock.Periodic (Periodic (Periodic)) where  -- base import Data.List.NonEmpty hiding (unfold) import Data.Maybe (fromMaybe)-import GHC.TypeLits (Nat, KnownNat, natVal)+import GHC.TypeLits (KnownNat, Nat, natVal)  -- dunai import Data.MonadicStreamFunction  -- rhine+import Control.Monad.Schedule import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy-import Control.Monad.Schedule  -- * The 'Periodic' clock --- | A clock whose tick lengths cycle through---   a (nonempty) list of type-level natural numbers.---   E.g. @Periodic '[1, 2]@ ticks at times 1, 3, 4, 5, 7, 8, etc.------   The waiting side effect is formal, in 'ScheduleT'.---   You can use e.g. 'runScheduleIO' to produce an actual delay.+{- | A clock whose tick lengths cycle through+   a (nonempty) list of type-level natural numbers.+   E.g. @Periodic '[1, 2]@ ticks at times 1, 3, 4, 5, 7, 8, etc.++   The waiting side effect is formal, in 'ScheduleT'.+   You can use e.g. 'runScheduleIO' to produce an actual delay.+-} data Periodic (v :: [Nat]) where   Periodic :: Periodic (n : ns) -instance (Monad m, NonemptyNatList v)-      => Clock (ScheduleT Integer m) (Periodic v) where+instance+  (Monad m, NonemptyNatList v) =>+  Clock (ScheduleT Integer m) (Periodic v)+  where   type Time (Periodic v) = Integer-  type Tag  (Periodic v) = ()-  initClock cl = return-    ( cycleS (theList cl) >>> withSideEffect wait >>> accumulateWith (+) 0 &&& arr (const ())-    , 0-    )+  type Tag (Periodic v) = ()+  initClock cl =+    return+      ( cycleS (theList cl) >>> withSideEffect wait >>> accumulateWith (+) 0 &&& arr (const ())+      , 0+      )  instance GetClockProxy (Periodic v) @@ -66,14 +70,16 @@ instance KnownNat n => NonemptyNatList '[n] where   theList cl = headCl cl :| [] -instance (KnownNat n1, KnownNat n2, NonemptyNatList (n2 : ns))-      => NonemptyNatList (n1 : n2 : ns) where+instance+  (KnownNat n1, KnownNat n2, NonemptyNatList (n2 : ns)) =>+  NonemptyNatList (n1 : n2 : ns)+  where   theList cl = headCl cl <| theList (tailCl cl) - -- * Utilities  -- TODO Port back to dunai when naming issues are resolved+ -- | Repeatedly outputs the values of a given list, in order. cycleS :: Monad m => NonEmpty a -> MSF m () a cycleS as = unfold (second (fromMaybe as) . uncons) as
src/FRP/Rhine/Clock/Proxy.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-}+ module FRP.Rhine.Clock.Proxy where  -- base@@ -11,20 +12,21 @@ import FRP.Rhine.Clock import FRP.Rhine.Schedule --- | Witnesses the structure of a clock type,---   in particular whether 'SequentialClock's or 'ParallelClock's are involved.+{- | Witnesses the structure of a clock type,+   in particular whether 'SequentialClock's or 'ParallelClock's are involved.+-} data ClockProxy cl where-  LeafProxy-    :: (cl ~ In cl, cl ~ Out cl)-    => ClockProxy cl-  SequentialProxy-    :: ClockProxy cl1-    -> ClockProxy cl2-    -> ClockProxy (SequentialClock m cl1 cl2)-  ParallelProxy-    :: ClockProxy clL-    -> ClockProxy clR-    -> ClockProxy (ParallelClock m clL clR)+  LeafProxy ::+    (cl ~ In cl, cl ~ Out cl) =>+    ClockProxy cl+  SequentialProxy ::+    ClockProxy cl1 ->+    ClockProxy cl2 ->+    ClockProxy (SequentialClock m cl1 cl2)+  ParallelProxy ::+    ClockProxy clL ->+    ClockProxy clR ->+    ClockProxy (ParallelClock m clL clR)  inProxy :: ClockProxy cl -> ClockProxy (In cl) inProxy LeafProxy = LeafProxy@@ -36,33 +38,35 @@ outProxy (SequentialProxy _ p2) = outProxy p2 outProxy (ParallelProxy pL pR) = ParallelProxy (outProxy pL) (outProxy pR) --- | Return the incoming tag, assuming that the incoming clock is ticked,---   and 'Nothing' otherwise.+{- | Return the incoming tag, assuming that the incoming clock is ticked,+   and 'Nothing' otherwise.+-} inTag :: ClockProxy cl -> Tag cl -> Maybe (Tag (In cl))-inTag (SequentialProxy p1 _) (Left  tag1) = inTag p1 tag1-inTag (SequentialProxy _  _) (Right _)    = Nothing-inTag (ParallelProxy pL _) (Left  tagL) = Left  <$> inTag pL tagL+inTag (SequentialProxy p1 _) (Left tag1) = inTag p1 tag1+inTag (SequentialProxy _ _) (Right _) = Nothing+inTag (ParallelProxy pL _) (Left tagL) = Left <$> inTag pL tagL inTag (ParallelProxy _ pR) (Right tagR) = Right <$> inTag pR tagR inTag LeafProxy tag = Just tag --- | Return the incoming tag, assuming that the outgoing clock is ticked,---   and 'Nothing' otherwise.+{- | Return the incoming tag, assuming that the outgoing clock is ticked,+   and 'Nothing' otherwise.+-} outTag :: ClockProxy cl -> Tag cl -> Maybe (Tag (Out cl))-outTag (SequentialProxy _ _ ) (Left  _)    = Nothing+outTag (SequentialProxy _ _) (Left _) = Nothing outTag (SequentialProxy _ p2) (Right tag2) = outTag p2 tag2-outTag (ParallelProxy pL _) (Left  tagL) = Left  <$> outTag pL tagL+outTag (ParallelProxy pL _) (Left tagL) = Left <$> outTag pL tagL outTag (ParallelProxy _ pR) (Right tagR) = Right <$> outTag pR tagR outTag LeafProxy tag = Just tag  -- TODO Should this be a superclass with default implementation of clocks? But then we have a circular dependency... -- No we don't, Schedule should not depend on clock (the type).+ -- | Clocks should be able to automatically generate a proxy for themselves. class GetClockProxy cl where   getClockProxy :: ClockProxy cl--  default getClockProxy-    :: (cl ~ In cl, cl ~ Out cl)-    => ClockProxy cl+  default getClockProxy ::+    (cl ~ In cl, cl ~ Out cl) =>+    ClockProxy cl   getClockProxy = LeafProxy  instance (GetClockProxy cl1, GetClockProxy cl2) => GetClockProxy (SequentialClock m cl1 cl2) where@@ -81,8 +85,8 @@   type Cl a :: Type    toClockProxy :: a -> ClockProxy (Cl a)--  default toClockProxy-    :: GetClockProxy (Cl a)-    => a -> ClockProxy (Cl a)+  default toClockProxy ::+    GetClockProxy (Cl a) =>+    a ->+    ClockProxy (Cl a)   toClockProxy _ = getClockProxy
src/FRP/Rhine/Clock/Realtime/Audio.hs view
@@ -1,8 +1,3 @@-{- |-Provides several clocks to use for audio processing,-for realtime as well as for batch/file processing.--}- {-# LANGUAGE Arrows #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-}@@ -12,24 +7,27 @@ -- {-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-} -- TODO Find out exact version of cabal? GHC? that have a problem with this -module FRP.Rhine.Clock.Realtime.Audio-  ( AudioClock (..)-  , AudioRate (..)-  , PureAudioClock (..)-  , PureAudioClockF-  , pureAudioClockF-  )-  where+{- |+Provides several clocks to use for audio processing,+for realtime as well as for batch/file processing.+-}+module FRP.Rhine.Clock.Realtime.Audio (+  AudioClock (..),+  AudioRate (..),+  PureAudioClock (..),+  PureAudioClockF,+  pureAudioClockF,+)+where  -- base-import GHC.Float       (double2Float)-import GHC.TypeLits    (Nat, natVal, KnownNat) import Data.Time.Clock+import GHC.Float (double2Float)+import GHC.TypeLits (KnownNat, Nat, natVal)  -- transformers import Control.Monad.IO.Class - -- dunai import Control.Monad.Trans.MSF.Except hiding (step) @@ -49,8 +47,8 @@ rateToIntegral Hz48000 = 48000 rateToIntegral Hz96000 = 96000 - -- TODO Test extensively+ {- | A clock for audio analysis and synthesis. It internally processes samples in buffers of size 'bufferSize',@@ -86,35 +84,37 @@ instance AudioClockRate Hz96000 where   theRate _ = Hz96000 --theBufferSize-  :: (KnownNat bufferSize, Integral a)-  => AudioClock rate bufferSize -> a+theBufferSize ::+  (KnownNat bufferSize, Integral a) =>+  AudioClock rate bufferSize ->+  a theBufferSize = fromInteger . natVal --instance (MonadIO m, KnownNat bufferSize, AudioClockRate rate)-      => Clock m (AudioClock rate bufferSize) where+instance+  (MonadIO m, KnownNat bufferSize, AudioClockRate rate) =>+  Clock m (AudioClock rate bufferSize)+  where   type Time (AudioClock rate bufferSize) = UTCTime-  type Tag  (AudioClock rate bufferSize) = Maybe Double+  type Tag (AudioClock rate bufferSize) = Maybe Double    initClock audioClock = do     let-      step       = picosecondsToDiffTime -- The only sufficiently precise conversion function-                     $ round (10 ^ (12 :: Integer) / theRateNum audioClock :: Double)+      step =+        picosecondsToDiffTime $ -- The only sufficiently precise conversion function+          round (10 ^ (12 :: Integer) / theRateNum audioClock :: Double)       bufferSize = theBufferSize audioClock        runningClock :: MonadIO m => UTCTime -> Maybe Double -> MSF m () (UTCTime, Maybe Double)       runningClock initialTime maybeWasLate = safely $ do         bufferFullTime <- try $ proc () -> do-          n <- count    -< ()+          n <- count -< ()           let nextTime = (realToFrac step * fromIntegral (n :: Int)) `addUTCTime` initialTime           _ <- throwOn' -< (n >= bufferSize, nextTime)-          returnA       -< (nextTime, if n == 0 then maybeWasLate else Nothing)+          returnA -< (nextTime, if n == 0 then maybeWasLate else Nothing)         currentTime <- once_ $ liftIO getCurrentTime         let           lateDiff = currentTime `diffTime` bufferFullTime-          late     = if lateDiff > 0 then Just lateDiff else Nothing+          late = if lateDiff > 0 then Just lateDiff else Nothing         safe $ runningClock bufferFullTime late     initialTime <- liftIO getCurrentTime     return@@ -141,26 +141,27 @@   thePureRateNum :: Num a => PureAudioClock rate -> a   thePureRateNum = fromInteger . thePureRateIntegral - instance (Monad m, PureAudioClockRate rate) => Clock m (PureAudioClock rate) where   type Time (PureAudioClock rate) = Double-  type Tag  (PureAudioClock rate) = ()+  type Tag (PureAudioClock rate) = () -  initClock audioClock = return-    ( arr (const (1 / thePureRateNum audioClock)) >>> sumS &&& arr (const ())-    , 0-    )+  initClock audioClock =+    return+      ( arr (const (1 / thePureRateNum audioClock)) >>> sumS &&& arr (const ())+      , 0+      )  instance GetClockProxy (PureAudioClock rate)  -- | A rescaled version of 'PureAudioClock' with 'TimeDomain' 'Float'. type PureAudioClockF (rate :: AudioRate) = RescaledClock (PureAudioClock rate) Float ---- | A rescaled version of 'PureAudioClock' with 'TimeDomain' 'Float',---   using 'double2Float' to rescale.+{- | A rescaled version of 'PureAudioClock' with 'TimeDomain' 'Float',+   using 'double2Float' to rescale.+-} pureAudioClockF :: PureAudioClockF rate-pureAudioClockF = RescaledClock-  { unscaledClock = PureAudioClock-  , rescale       = double2Float-  }+pureAudioClockF =+  RescaledClock+    { unscaledClock = PureAudioClock+    , rescale = double2Float+    }
src/FRP/Rhine/Clock/Realtime/Busy.hs view
@@ -1,7 +1,7 @@-{- | A "'Busy'" clock that ticks without waiting. -}- {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}++-- | A "'Busy'" clock that ticks without waiting. module FRP.Rhine.Clock.Realtime.Busy where  -- base@@ -20,13 +20,13 @@  instance Clock IO Busy where   type Time Busy = UTCTime-  type Tag  Busy = ()+  type Tag Busy = ()    initClock _ = do     initialTime <- getCurrentTime     return       ( constM getCurrentTime-        &&& arr (const ())+          &&& arr (const ())       , initialTime       ) 
src/FRP/Rhine/Clock/Realtime/Event.hs view
@@ -1,3 +1,10 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+ {- | This module provides two things: @@ -15,22 +22,17 @@  A simple example using events and threads can be found in rhine-examples. -}--{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}-module FRP.Rhine.Clock.Realtime.Event-  ( module FRP.Rhine.Clock.Realtime.Event-  , module Control.Monad.IO.Class-  , newChan-  )-  where+module FRP.Rhine.Clock.Realtime.Event (+  module FRP.Rhine.Clock.Realtime.Event,+  module Control.Monad.IO.Class,+  newChan,+)+where  -- base import Control.Concurrent.Chan++-- time import Data.Time.Clock  -- deepseq@@ -41,21 +43,21 @@ import Control.Monad.Trans.Reader  -- rhine-import FRP.Rhine.Clock.Proxy import FRP.Rhine.ClSF+import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.Schedule import FRP.Rhine.Schedule.Concurrently -- -- * Monads allowing for event emission and handling  -- | A monad transformer in which events can be emitted onto a 'Chan'. type EventChanT event m = ReaderT (Chan event) m --- | Escape the 'EventChanT' layer by explicitly providing a channel---   over which events are sent.---   Often this is not needed, and 'runEventChanT' can be used instead.+{- | Escape the 'EventChanT' layer by explicitly providing a channel+   over which events are sent.+   Often this is not needed, and 'runEventChanT' can be used instead.+-} withChan :: Chan event -> EventChanT event m a -> m a withChan = flip runReaderT @@ -87,11 +89,11 @@ pass the channel to every behaviour or 'ClSF' that wants to emit events, and, by using 'eventClockOn', to every clock that should tick on the event. -}-withChanS-  :: Monad m-  => Chan event-  -> ClSF (EventChanT event m) cl a b-  -> ClSF m cl a b+withChanS ::+  Monad m =>+  Chan event ->+  ClSF (EventChanT event m) cl a b ->+  ClSF m cl a b withChanS = flip runReaderS_  -- * Event emission@@ -118,29 +120,30 @@  -- | Like 'emit', but completely evaluates the event before emitting it. emit' :: (NFData event, MonadIO m) => event -> EventChanT event m ()-emit' event = event `deepseq` do-  chan <- ask-  liftIO $ writeChan chan event+emit' event =+  event `deepseq` do+    chan <- ask+    liftIO $ writeChan chan event  -- | Like 'emitS', but completely evaluates the event before emitting it. emitS' :: (NFData event, MonadIO m) => ClSF (EventChanT event m) cl event () emitS' = arrMCl emit'  -- | Like 'emitSMaybe', but completely evaluates the event before emitting it.-emitSMaybe'-  :: (NFData event, MonadIO m)-  => ClSF (EventChanT event m) cl (Maybe event) ()+emitSMaybe' ::+  (NFData event, MonadIO m) =>+  ClSF (EventChanT event m) cl (Maybe event) () emitSMaybe' = mapMaybe emitS' >>> arr (const ()) - -- * Event clocks and schedules --- | A clock that ticks whenever an @event@ is emitted.---   It is not yet bound to a specific channel,---   since ideally, the correct channel is created automatically---   by 'runEventChanT'.---   If you want to create the channel manually and bind the clock to it,---   use 'eventClockOn'.+{- | A clock that ticks whenever an @event@ is emitted.+   It is not yet bound to a specific channel,+   since ideally, the correct channel is created automatically+   by 'runEventChanT'.+   If you want to create the channel manually and bind the clock to it,+   use 'eventClockOn'.+-} data EventClock event = EventClock  instance Semigroup (EventClock event) where@@ -148,31 +151,33 @@  instance MonadIO m => Clock (EventChanT event m) (EventClock event) where   type Time (EventClock event) = UTCTime-  type Tag  (EventClock event) = event+  type Tag (EventClock event) = event   initClock _ = do     initialTime <- liftIO getCurrentTime     return       ( constM $ do-          chan  <- ask+          chan <- ask           event <- liftIO $ readChan chan-          time  <- liftIO getCurrentTime+          time <- liftIO getCurrentTime           return (time, event)       , initialTime       )  instance GetClockProxy (EventClock event) --- | Create an event clock that is bound to a specific event channel.---   This is usually only useful if you can't apply 'runEventChanT'---   to the main loop (see 'withChanS').-eventClockOn-  :: MonadIO m-  => Chan event-  -> HoistClock (EventChanT event m) m (EventClock event)-eventClockOn chan = HoistClock-  { unhoistedClock = EventClock-  , monadMorphism  = withChan chan-  }+{- | Create an event clock that is bound to a specific event channel.+   This is usually only useful if you can't apply 'runEventChanT'+   to the main loop (see 'withChanS').+-}+eventClockOn ::+  MonadIO m =>+  Chan event ->+  HoistClock (EventChanT event m) m (EventClock event)+eventClockOn chan =+  HoistClock+    { unhoistedClock = EventClock+    , monadMorphism = withChan chan+    }  {- | Given two clocks with an 'EventChanT' layer directly atop the 'IO' monad,@@ -187,10 +192,10 @@ * An event clock and other event-unaware clocks in the 'IO' monad,   which are lifted using 'liftClock'. -}-concurrentlyWithEvents-  :: ( Time cl1 ~ Time cl2-     , Clock (EventChanT event IO) cl1-     , Clock (EventChanT event IO) cl2-     )-  => Schedule (EventChanT event IO) cl1 cl2+concurrentlyWithEvents ::+  ( Time cl1 ~ Time cl2+  , Clock (EventChanT event IO) cl1+  , Clock (EventChanT event IO) cl2+  ) =>+  Schedule (EventChanT event IO) cl1 cl2 concurrentlyWithEvents = readerSchedule concurrently
src/FRP/Rhine/Clock/Realtime/Millisecond.hs view
@@ -1,30 +1,29 @@-{- |-Provides a clock that ticks at every multiple of a fixed number of milliseconds.--}- {-# LANGUAGE DataKinds #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}++{- |+Provides a clock that ticks at every multiple of a fixed number of milliseconds.+-} module FRP.Rhine.Clock.Realtime.Millisecond where  -- base+import Control.Concurrent (threadDelay) import Data.Maybe (fromMaybe) import Data.Time.Clock-import Control.Concurrent (threadDelay) import GHC.TypeLits --- fixed-vector+-- vector-sized import Data.Vector.Sized (Vector, fromList)  -- rhine import FRP.Rhine.Clock-import FRP.Rhine.Clock.Proxy import FRP.Rhine.Clock.FixedStep-import FRP.Rhine.Schedule+import FRP.Rhine.Clock.Proxy import FRP.Rhine.ResamplingBuffer-import FRP.Rhine.ResamplingBuffer.Util import FRP.Rhine.ResamplingBuffer.Collect+import FRP.Rhine.ResamplingBuffer.Util+import FRP.Rhine.Schedule  {- | A clock ticking every 'n' milliseconds,@@ -38,30 +37,31 @@ and 'False' a lag. -} newtype Millisecond (n :: Nat) = Millisecond (RescaledClockS IO (FixedStep n) UTCTime Bool)+ -- TODO Consider changing the tag to Maybe Double  instance Clock IO (Millisecond n) where   type Time (Millisecond n) = UTCTime-  type Tag  (Millisecond n) = Bool+  type Tag (Millisecond n) = Bool   initClock (Millisecond cl) = initClock cl  instance GetClockProxy (Millisecond n) --- | This implementation measures the time after each tick,---   and waits for the remaining time until the next tick.---   If the next tick should already have occurred,---   the tag is set to 'False', representing a failed real time attempt.----   Note that this clock internally uses 'threadDelay' which can block---   for quite a lot longer than the requested time, which can cause---   the clock to miss one or more ticks when using low values of 'n'.---   When using 'threadDelay', the difference between the real wait time---   and the requested wait time will be larger when using---   the '-threaded' ghc option (around 800 microseconds) than when not using---   this option (around 100 microseconds). For low values of @n@ it is recommended---   that '-threaded' not be used in order to miss less ticks. The clock will adjust---   the wait time, up to no wait time at all, to catch up when a tick is missed.+{- | This implementation measures the time after each tick,+   and waits for the remaining time until the next tick.+   If the next tick should already have occurred,+   the tag is set to 'False', representing a failed real time attempt. +   Note that this clock internally uses 'threadDelay' which can block+   for quite a lot longer than the requested time, which can cause+   the clock to miss one or more ticks when using low values of 'n'.+   When using 'threadDelay', the difference between the real wait time+   and the requested wait time will be larger when using+   the '-threaded' ghc option (around 800 microseconds) than when not using+   this option (around 100 microseconds). For low values of @n@ it is recommended+   that '-threaded' not be used in order to miss less ticks. The clock will adjust+   the wait time, up to no wait time at all, to catch up when a tick is missed.+-} waitClock :: KnownNat n => Millisecond n waitClock = Millisecond $ RescaledClockS FixedStep $ \_ -> do   initTime <- getCurrentTime@@ -70,29 +70,31 @@       beforeSleep <- getCurrentTime       let         diff :: Double-        diff      = realToFrac $ beforeSleep `diffUTCTime` initTime+        diff = realToFrac $ beforeSleep `diffUTCTime` initTime         remaining = fromInteger $ n * 1000 - round (diff * 1000000)       threadDelay remaining-      now         <- getCurrentTime -- TODO Test whether this is a performance penalty+      now <- getCurrentTime -- TODO Test whether this is a performance penalty       return (now, remaining > 0)   return (runningClock, initTime) - -- TODO It would be great if this could be directly implemented in terms of downsampleFixedStep-downsampleMillisecond-  :: (KnownNat n, Monad m)-  => ResamplingBuffer m (Millisecond k) (Millisecond (n * k)) a (Vector n a)+downsampleMillisecond ::+  (KnownNat n, Monad m) =>+  ResamplingBuffer m (Millisecond k) (Millisecond (n * k)) a (Vector n a) downsampleMillisecond = collect >>-^ arr (fromList >>> assumeSize)   where-    assumeSize = fromMaybe $ error $ unwords-      [ "You are using an incorrectly implemented schedule"-      , "for two Millisecond clocks."-      , "Use a correct schedule like downsampleMillisecond."-      ]+    assumeSize =+      fromMaybe $+        error $+          unwords+            [ "You are using an incorrectly implemented schedule"+            , "for two Millisecond clocks."+            , "Use a correct schedule like downsampleMillisecond."+            ]  -- | Two 'Millisecond' clocks can always be scheduled deterministically. scheduleMillisecond :: Schedule IO (Millisecond n1) (Millisecond n2) scheduleMillisecond = Schedule initSchedule'   where-    initSchedule' (Millisecond cl1) (Millisecond cl2)-      = initSchedule (rescaledScheduleS scheduleFixedStep) cl1 cl2+    initSchedule' (Millisecond cl1) (Millisecond cl2) =+      initSchedule (rescaledScheduleS scheduleFixedStep) cl1 cl2
src/FRP/Rhine/Clock/Realtime/Stdin.hs view
@@ -1,12 +1,12 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+ {- | In Rhine, event sources are clocks, and so is the console. If this clock is used, every input line on the console triggers one tick of the 'StdinClock'. -}--{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.Clock.Realtime.Stdin where  -- time@@ -27,7 +27,7 @@  instance MonadIO m => Clock m StdinClock where   type Time StdinClock = UTCTime-  type Tag  StdinClock = String+  type Tag StdinClock = String    initClock _ = do     initialTime <- liftIO getCurrentTime
src/FRP/Rhine/Clock/Select.hs view
@@ -1,3 +1,10 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+ {- | In the Rhine philosophy, _event sources are clocks_. Often, we want to extract certain subevents from event sources,@@ -5,13 +12,6 @@ This module provides a general purpose selection clock that ticks only on certain subevents. -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.Clock.Select where  -- rhine@@ -25,82 +25,94 @@ -- base import Data.Maybe (catMaybes, maybeToList) --- | A clock that selects certain subevents of type 'a',---   from the tag of a main clock.------   If two 'SelectClock's would tick on the same type of subevents,---   but should not have the same type,---   one should @newtype@ the subevent.+{- | A clock that selects certain subevents of type 'a',+   from the tag of a main clock.++   If two 'SelectClock's would tick on the same type of subevents,+   but should not have the same type,+   one should @newtype@ the subevent.+-} data SelectClock cl a = SelectClock-  { mainClock :: cl -- ^ The main clock+  { mainClock :: cl+  -- ^ The main clock   -- | Return 'Nothing' if no tick of the subclock is required,   --   or 'Just a' if the subclock should tick, with tag 'a'.-  , select    :: Tag cl -> Maybe a+  , select :: Tag cl -> Maybe a   }  instance (Semigroup a, Semigroup cl) => Semigroup (SelectClock cl a) where-  cl1 <> cl2 = SelectClock-    { mainClock = mainClock cl1 <> mainClock cl2-    , select = \tag -> select cl1 tag <> select cl2 tag-    }+  cl1 <> cl2 =+    SelectClock+      { mainClock = mainClock cl1 <> mainClock cl2+      , select = \tag -> select cl1 tag <> select cl2 tag+      }  instance (Monoid cl, Semigroup a) => Monoid (SelectClock cl a) where-  mempty = SelectClock-    { mainClock = mempty-    , select = const mempty-    }-+  mempty =+    SelectClock+      { mainClock = mempty+      , select = const mempty+      }  instance (Monad m, Clock m cl) => Clock m (SelectClock cl a) where   type Time (SelectClock cl a) = Time cl-  type Tag  (SelectClock cl a) = a+  type Tag (SelectClock cl a) = a   initClock SelectClock {..} = do     (runningClock, initialTime) <- initClock mainClock     let       runningSelectClock = filterS $ proc _ -> do         (time, tag) <- runningClock -< ()-        returnA                     -< (time, ) <$> select tag+        returnA -< (time,) <$> select tag     return (runningSelectClock, initialTime)  instance GetClockProxy (SelectClock cl a) --- | A universal schedule for two subclocks of the same main clock.---   The main clock must be a 'Semigroup' (e.g. a singleton).-schedSelectClocks-  :: (Monad m, Semigroup cl, Clock m cl)-  => Schedule m (SelectClock cl a) (SelectClock cl b)+{- | A universal schedule for two subclocks of the same main clock.+   The main clock must be a 'Semigroup' (e.g. a singleton).+-}+schedSelectClocks ::+  (Monad m, Semigroup cl, Clock m cl) =>+  Schedule m (SelectClock cl a) (SelectClock cl b) schedSelectClocks = Schedule {..}   where     initSchedule subClock1 subClock2 = do-      (runningClock, initialTime) <- initClock-        $ mainClock subClock1 <> mainClock subClock2+      (runningClock, initialTime) <-+        initClock $+          mainClock subClock1 <> mainClock subClock2       let         runningSelectClocks = concatS $ proc _ -> do           (time, tag) <- runningClock -< ()-          returnA                     -< catMaybes-            [ (time, ) . Left  <$> select subClock1 tag-            , (time, ) . Right <$> select subClock2 tag ]+          returnA+            -<+              catMaybes+                [ (time,) . Left <$> select subClock1 tag+                , (time,) . Right <$> select subClock2 tag+                ]       return (runningSelectClocks, initialTime)  -- | A universal schedule for a subclock and its main clock.-schedSelectClockAndMain-  :: (Monad m, Semigroup cl, Clock m cl)-  => Schedule m cl (SelectClock cl a)+schedSelectClockAndMain ::+  (Monad m, Semigroup cl, Clock m cl) =>+  Schedule m cl (SelectClock cl a) schedSelectClockAndMain = Schedule {..}   where     initSchedule mainClock' SelectClock {..} = do-      (runningClock, initialTime) <- initClock-        $ mainClock' <> mainClock+      (runningClock, initialTime) <-+        initClock $+          mainClock' <> mainClock       let         runningSelectClock = concatS $ proc _ -> do           (time, tag) <- runningClock -< ()-          returnA                     -< catMaybes-            [ Just (time, Left tag)-            , (time, ) . Right <$> select tag ]+          returnA+            -<+              catMaybes+                [ Just (time, Left tag)+                , (time,) . Right <$> select tag+                ]       return (runningSelectClock, initialTime) ---- | Helper function that runs an 'MSF' with 'Maybe' output---   until it returns a value.+{- | Helper function that runs an 'MSF' with 'Maybe' output+   until it returns a value.+-} filterS :: Monad m => MSF m () (Maybe b) -> MSF m () b filterS = concatS . (>>> arr maybeToList)
src/FRP/Rhine/Clock/Util.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-}+ module FRP.Rhine.Clock.Util where  -- time-domain@@ -11,16 +12,20 @@  -- * Auxiliary definitions and utilities --- | Given a clock value and an initial time,---   generate a stream of time stamps.-genTimeInfo-  :: (Monad m, Clock m cl)-  => ClockProxy cl -> Time cl-  -> MSF m (Time cl, Tag cl) (TimeInfo cl)+{- | Given a clock value and an initial time,+   generate a stream of time stamps.+-}+genTimeInfo ::+  (Monad m, Clock m cl) =>+  ClockProxy cl ->+  Time cl ->+  MSF m (Time cl, Tag cl) (TimeInfo cl) genTimeInfo _ initialTime = proc (absolute, tag) -> do   lastTime <- iPre initialTime -< absolute-  returnA                      -< TimeInfo-    { sinceLast = absolute `diffTime` lastTime-    , sinceInit = absolute `diffTime` initialTime-    , ..-    }+  returnA+    -<+      TimeInfo+        { sinceLast = absolute `diffTime` lastTime+        , sinceInit = absolute `diffTime` initialTime+        , ..+        }
src/FRP/Rhine/Reactimation.hs view
@@ -1,18 +1,18 @@+{-# LANGUAGE GADTs #-}+ {- | Run closed 'Rhine's (which are signal functions together with matching clocks) as main loops. -}--{-# LANGUAGE GADTs #-} module FRP.Rhine.Reactimation where  -- dunai import Data.MonadicStreamFunction.InternalCore  -- rhine+import FRP.Rhine.ClSF.Core import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy-import FRP.Rhine.ClSF.Core import FRP.Rhine.Reactimation.Combinators import FRP.Rhine.Schedule import FRP.Rhine.Type@@ -46,24 +46,32 @@ main = flow $ mainSF @@ clock @ -}+ -- TODO Can we chuck the constraints into Clock m cl?-flow-  :: ( Monad m, Clock m cl-     , GetClockProxy cl-     , Time cl ~ Time (In  cl)-     , Time cl ~ Time (Out cl)-     )-  => Rhine m cl () () -> m ()+flow ::+  ( Monad m+  , Clock m cl+  , GetClockProxy cl+  , Time cl ~ Time (In cl)+  , Time cl ~ Time (Out cl)+  ) =>+  Rhine m cl () () ->+  m () flow rhine = do   msf <- eraseClock rhine   reactimate $ msf >>> arr (const ()) --- | Run a synchronous 'ClSF' with its clock as a main loop,---   similar to Yampa's, or Dunai's, 'reactimate'.-reactimateCl-  :: ( Monad m, Clock m cl-     , GetClockProxy cl-     , cl ~ In  cl, cl ~ Out cl-     )-  => cl -> ClSF m cl () () -> m ()+{- | Run a synchronous 'ClSF' with its clock as a main loop,+   similar to Yampa's, or Dunai's, 'reactimate'.+-}+reactimateCl ::+  ( Monad m+  , Clock m cl+  , GetClockProxy cl+  , cl ~ In cl+  , cl ~ Out cl+  ) =>+  cl ->+  ClSF m cl () () ->+  m () reactimateCl cl clsf = flow $ clsf @@ cl
src/FRP/Rhine/Reactimation/ClockErasure.hs view
@@ -1,13 +1,14 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE TupleSections #-}+ {- | Translate clocked signal processing components to stream functions without explicit clock types.  This module is not meant to be used externally, and is thus not exported from 'FRP.Rhine'. -}-{-# LANGUAGE Arrows #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE TupleSections #-} module FRP.Rhine.Reactimation.ClockErasure where  -- base@@ -18,42 +19,45 @@ import Data.MonadicStreamFunction  -- rhine++import FRP.Rhine.ClSF hiding (runReaderS) import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy import FRP.Rhine.Clock.Util-import FRP.Rhine.ClSF hiding (runReaderS) import FRP.Rhine.ResamplingBuffer import FRP.Rhine.SN --- | Run a clocked signal function as a monadic stream function,---   accepting the timestamps and tags as explicit inputs.-eraseClockClSF-  :: (Monad m, Clock m cl)-  => ClockProxy cl -> Time cl-  -> ClSF m cl a b-  -> MSF m (Time cl, Tag cl, a) b+{- | Run a clocked signal function as a monadic stream function,+   accepting the timestamps and tags as explicit inputs.+-}+eraseClockClSF ::+  (Monad m, Clock m cl) =>+  ClockProxy cl ->+  Time cl ->+  ClSF m cl a b ->+  MSF m (Time cl, Tag cl, a) b eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do   timeInfo <- genTimeInfo proxy initialTime -< (time, tag)-  runReaderS clsf                           -< (timeInfo, a)+  runReaderS clsf -< (timeInfo, a) --- | Run a signal network as a monadic stream function.------   Depending on the incoming clock,---   input data may need to be provided,---   and depending on the outgoing clock,---   output data may be generated.---   There are thus possible invalid inputs,---   which 'eraseClockSN' does not gracefully handle.-eraseClockSN-  :: (Monad m, Clock m cl, GetClockProxy cl)-  => Time cl-  -> SN m cl a b-  -> MSF m (Time cl, Tag cl, Maybe a) (Maybe b)+{- | Run a signal network as a monadic stream function. +   Depending on the incoming clock,+   input data may need to be provided,+   and depending on the outgoing clock,+   output data may be generated.+   There are thus possible invalid inputs,+   which 'eraseClockSN' does not gracefully handle.+-}+eraseClockSN ::+  (Monad m, Clock m cl, GetClockProxy cl) =>+  Time cl ->+  SN m cl a b ->+  MSF m (Time cl, Tag cl, Maybe a) (Maybe b) -- A synchronous signal network is run by erasing the clock from the clocked signal function. eraseClockSN initialTime sn@(Synchronous clsf) = proc (time, tag, Just a) -> do   b <- eraseClockClSF (toClockProxy sn) initialTime clsf -< (time, tag, a)-  returnA                                                -< Just b+  returnA -< Just b  -- A sequentially composed signal network may either be triggered in its first component, -- or its second component. In either case,@@ -64,91 +68,95 @@   let     proxy1 = toClockProxy sn1     proxy2 = toClockProxy sn2-  in proc (time, tag, maybeA) -> do-  resBufIn <- case tag of-    Left  tagL -> do-      maybeB <- eraseClockSN initialTime sn1 -< (time, tagL, maybeA)-      returnA -< Left <$> ((time, , ) <$> outTag proxy1 tagL <*> maybeB)-    Right tagR -> do-      returnA -< Right . (time, ) <$> inTag proxy2 tagR-  maybeC <- mapMaybeS $ eraseClockResBuf (outProxy proxy1) (inProxy proxy2) initialTime resBuf -< resBufIn-  case tag of-    Left  _    -> do-      returnA -< Nothing-    Right tagR -> do-      eraseClockSN initialTime sn2 -< (time, tagR, join maybeC)-+   in+    proc (time, tag, maybeA) -> do+      resBufIn <- case tag of+        Left tagL -> do+          maybeB <- eraseClockSN initialTime sn1 -< (time, tagL, maybeA)+          returnA -< Left <$> ((time,,) <$> outTag proxy1 tagL <*> maybeB)+        Right tagR -> do+          returnA -< Right . (time,) <$> inTag proxy2 tagR+      maybeC <- mapMaybeS $ eraseClockResBuf (outProxy proxy1) (inProxy proxy2) initialTime resBuf -< resBufIn+      case tag of+        Left _ -> do+          returnA -< Nothing+        Right tagR -> do+          eraseClockSN initialTime sn2 -< (time, tagR, join maybeC) eraseClockSN initialTime (Parallel snL snR) = proc (time, tag, maybeA) -> do   case tag of-    Left  tagL -> eraseClockSN initialTime snL -< (time, tagL, maybeA)+    Left tagL -> eraseClockSN initialTime snL -< (time, tagL, maybeA)     Right tagR -> eraseClockSN initialTime snR -< (time, tagR, maybeA)- eraseClockSN initialTime (Postcompose sn clsf) =   let     proxy = toClockProxy sn-  in proc input@(time, tag, _) -> do-  bMaybe <- eraseClockSN initialTime sn -< input-  mapMaybeS $ eraseClockClSF (outProxy proxy) initialTime clsf -< (time, , ) <$> outTag proxy tag <*> bMaybe-+   in+    proc input@(time, tag, _) -> do+      bMaybe <- eraseClockSN initialTime sn -< input+      mapMaybeS $ eraseClockClSF (outProxy proxy) initialTime clsf -< (time,,) <$> outTag proxy tag <*> bMaybe eraseClockSN initialTime (Precompose clsf sn) =   let     proxy = toClockProxy sn-  in proc (time, tag, aMaybe) -> do-  bMaybe <- mapMaybeS $ eraseClockClSF (inProxy proxy) initialTime clsf -< (time, , ) <$> inTag proxy tag <*> aMaybe-  eraseClockSN initialTime sn -< (time, tag, bMaybe)-+   in+    proc (time, tag, aMaybe) -> do+      bMaybe <- mapMaybeS $ eraseClockClSF (inProxy proxy) initialTime clsf -< (time,,) <$> inTag proxy tag <*> aMaybe+      eraseClockSN initialTime sn -< (time, tag, bMaybe) eraseClockSN initialTime (Feedback buf0 sn) =   let     proxy = toClockProxy sn-  in feedback buf0 $ proc ((time, tag, aMaybe), buf) -> do-  (cMaybe, buf') <- case inTag proxy tag of-    Nothing -> do-      returnA -< (Nothing, buf)-    Just tagIn -> do-      timeInfo <- genTimeInfo (inProxy proxy) initialTime -< (time, tagIn)-      (c, buf') <- arrM $ uncurry get -< (buf, timeInfo)-      returnA -< (Just c, buf')-  bdMaybe <- eraseClockSN initialTime sn -< (time, tag, (, ) <$> aMaybe <*> cMaybe)-  case (, ) <$> outTag proxy tag <*> bdMaybe of-    Nothing -> do-      returnA -< (Nothing, buf')-    Just (tagOut, (b, d)) -> do-      timeInfo <- genTimeInfo (outProxy proxy) initialTime -< (time, tagOut)-      buf'' <- arrM $ uncurry $ uncurry put -< ((buf', timeInfo), d)-      returnA -< (Just b, buf'')-+   in+    feedback buf0 $ proc ((time, tag, aMaybe), buf) -> do+      (cMaybe, buf') <- case inTag proxy tag of+        Nothing -> do+          returnA -< (Nothing, buf)+        Just tagIn -> do+          timeInfo <- genTimeInfo (inProxy proxy) initialTime -< (time, tagIn)+          (c, buf') <- arrM $ uncurry get -< (buf, timeInfo)+          returnA -< (Just c, buf')+      bdMaybe <- eraseClockSN initialTime sn -< (time, tag, (,) <$> aMaybe <*> cMaybe)+      case (,) <$> outTag proxy tag <*> bdMaybe of+        Nothing -> do+          returnA -< (Nothing, buf')+        Just (tagOut, (b, d)) -> do+          timeInfo <- genTimeInfo (outProxy proxy) initialTime -< (time, tagOut)+          buf'' <- arrM $ uncurry $ uncurry put -< ((buf', timeInfo), d)+          returnA -< (Just b, buf'') eraseClockSN initialTime (FirstResampling sn buf) =   let     proxy = toClockProxy sn-  in proc (time, tag, acMaybe) -> do-    bMaybe <- eraseClockSN initialTime sn -< (time, tag, fst <$> acMaybe)-    let-      resBufInput = case (inTag proxy tag, outTag proxy tag, snd <$> acMaybe) of-        (Just tagIn, _, Just c) -> Just $ Left (time, tagIn, c)-        (_, Just tagOut, _) -> Just $ Right (time, tagOut)-        _ -> Nothing-    dMaybe <- mapMaybeS $ eraseClockResBuf (inProxy proxy) (outProxy proxy) initialTime buf -< resBufInput-    returnA -< (,) <$> bMaybe <*> join dMaybe+   in+    proc (time, tag, acMaybe) -> do+      bMaybe <- eraseClockSN initialTime sn -< (time, tag, fst <$> acMaybe)+      let+        resBufInput = case (inTag proxy tag, outTag proxy tag, snd <$> acMaybe) of+          (Just tagIn, _, Just c) -> Just $ Left (time, tagIn, c)+          (_, Just tagOut, _) -> Just $ Right (time, tagOut)+          _ -> Nothing+      dMaybe <- mapMaybeS $ eraseClockResBuf (inProxy proxy) (outProxy proxy) initialTime buf -< resBufInput+      returnA -< (,) <$> bMaybe <*> join dMaybe --- | Translate a resampling buffer into a monadic stream function.------   The input decides whether the buffer is to accept input or has to produce output.---   (In the latter case, only time information is provided.)-eraseClockResBuf-  :: ( Monad m-     , Clock m cl1, Clock m cl2-     , Time cl1 ~ Time cl2-     )-  => ClockProxy cl1 -> ClockProxy cl2 -> Time cl1-  -> ResBuf m cl1 cl2 a b-  -> MSF m (Either (Time cl1, Tag cl1, a) (Time cl2, Tag cl2)) (Maybe b)+{- | Translate a resampling buffer into a monadic stream function.++   The input decides whether the buffer is to accept input or has to produce output.+   (In the latter case, only time information is provided.)+-}+eraseClockResBuf ::+  ( Monad m+  , Clock m cl1+  , Clock m cl2+  , Time cl1 ~ Time cl2+  ) =>+  ClockProxy cl1 ->+  ClockProxy cl2 ->+  Time cl1 ->+  ResBuf m cl1 cl2 a b ->+  MSF m (Either (Time cl1, Tag cl1, a) (Time cl2, Tag cl2)) (Maybe b) eraseClockResBuf proxy1 proxy2 initialTime resBuf0 = feedback resBuf0 $ proc (input, resBuf) -> do   case input of     Left (time1, tag1, a) -> do-      timeInfo1 <- genTimeInfo proxy1 initialTime   -< (time1, tag1)-      resBuf'   <- arrM (uncurry $ uncurry put)     -< ((resBuf, timeInfo1), a)-      returnA                                       -< (Nothing, resBuf')+      timeInfo1 <- genTimeInfo proxy1 initialTime -< (time1, tag1)+      resBuf' <- arrM (uncurry $ uncurry put) -< ((resBuf, timeInfo1), a)+      returnA -< (Nothing, resBuf')     Right (time2, tag2) -> do-      timeInfo2    <- genTimeInfo proxy2 initialTime -< (time2, tag2)-      (b, resBuf') <- arrM (uncurry get)             -< (resBuf, timeInfo2)-      returnA                                        -< (Just b, resBuf')+      timeInfo2 <- genTimeInfo proxy2 initialTime -< (time2, tag2)+      (b, resBuf') <- arrM (uncurry get) -< (resBuf, timeInfo2)+      returnA -< (Just b, resBuf')
src/FRP/Rhine/Reactimation/Combinators.hs view
@@ -1,3 +1,7 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ {- | Combinators to create 'Rhine's (main programs) from basic components such as 'ClSF's, clocks, 'ResamplingBuffer's and 'Schedule's.@@ -11,43 +15,44 @@ * @*@ composes parallely. * @>@ composes sequentially. -}--{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}- module FRP.Rhine.Reactimation.Combinators where - -- rhine+import FRP.Rhine.ClSF.Core import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy-import FRP.Rhine.ClSF.Core import FRP.Rhine.ResamplingBuffer-import FRP.Rhine.Schedule import FRP.Rhine.SN import FRP.Rhine.SN.Combinators+import FRP.Rhine.Schedule import FRP.Rhine.Type - -- * Combinators and syntactic sugar for high-level composition of signal networks. - infix 5 @@--- | Create a synchronous 'Rhine' by combining a clocked signal function with a matching clock.---   Synchronicity is ensured by requiring that data enters (@In cl@)---   and leaves (@Out cl@) the system at the same as it is processed (@cl@).-(@@) :: ( cl ~ In cl-        , cl ~ Out cl )-     => ClSF m cl a b -> cl -> Rhine m cl a b++{- FOURMOLU_DISABLE -}+{- | Create a synchronous 'Rhine' by combining a clocked signal function with a matching clock.+   Synchronicity is ensured by requiring that data enters (@In cl@)+   and leaves (@Out cl@) the system at the same as it is processed (@cl@).+-}+(@@) ::+  ( cl ~ In cl+  , cl ~ Out cl+  ) =>+  ClSF  m cl a b ->+          cl     ->+  Rhine m cl a b (@@) = Rhine . Synchronous +{- | A point at which sequential asynchronous composition+   ("resampling") of signal networks can happen.+-}+data ResamplingPoint m cla clb a b+  = ResamplingPoint+      (ResamplingBuffer m (Out cla) (In clb) a b)+      (Schedule m cla clb) --- | A point at which sequential asynchronous composition---   ("resampling") of signal networks can happen.-data ResamplingPoint m cla clb a b = ResamplingPoint-  (ResamplingBuffer m (Out cla) (In clb) a b)-  (Schedule m cla clb) -- TODO Make a record out of it? -- TODO This is aesthetically displeasing. --      For the buffer, the associativity doesn't matter, but for the Schedule,@@ -57,21 +62,26 @@  -- | Syntactic sugar for 'ResamplingPoint'. infix 8 -@--(-@-) :: ResamplingBuffer m (Out cl1) (In cl2) a b-      -> Schedule         m      cl1      cl2-      -> ResamplingPoint  m      cl1      cl2  a b+(-@-) ::+  ResamplingBuffer m (Out cl1) (In cl2) a b ->+  Schedule         m      cl1      cl2      ->+  ResamplingPoint  m      cl1      cl2  a b (-@-) = ResamplingPoint --- | A purely syntactical convenience construction---   enabling quadruple syntax for sequential composition, as described below.+{- | A purely syntactical convenience construction+   enabling quadruple syntax for sequential composition, as described below.+-} infix 2 >---data RhineAndResamplingPoint m cl1 cl2 a c = forall b.-     RhineAndResamplingPoint (Rhine m cl1 a b) (ResamplingPoint m cl1 cl2 b c) +data RhineAndResamplingPoint m cl1 cl2 a c+  = forall b.+    RhineAndResamplingPoint (Rhine m cl1 a b) (ResamplingPoint m cl1 cl2 b c)+ -- | Syntactic sugar for 'RhineAndResamplingPoint'.-(>--) :: Rhine                   m cl1     a b-      -> ResamplingPoint         m cl1 cl2   b c-      -> RhineAndResamplingPoint m cl1 cl2 a   c+(>--) ::+  Rhine                   m cl1     a b   ->+  ResamplingPoint         m cl1 cl2   b c ->+  RhineAndResamplingPoint m cl1 cl2 a   c (>--) = RhineAndResamplingPoint  {- | The combinators for sequential composition allow for the following syntax:@@ -94,18 +104,19 @@ @ -} infixr 1 -->-(-->) :: ( Clock m cl1-         , Clock m cl2-         , Time cl1 ~ Time cl2-         , Time (Out cl1) ~ Time cl1-         , Time (In  cl2) ~ Time cl2-         , Clock m (Out cl1), Clock m (Out cl2)-         , Clock m (In  cl1), Clock m (In  cl2)-         , GetClockProxy cl1, GetClockProxy cl2-         )-      => RhineAndResamplingPoint   m cl1 cl2  a b-      -> Rhine m                         cl2    b c-      -> Rhine m  (SequentialClock m cl1 cl2) a   c+(-->) ::+  ( Clock m cl1+  , Clock m cl2+  , Time cl1 ~ Time cl2+  , Time (Out cl1) ~ Time cl1+  , Time (In  cl2) ~ Time cl2+  , Clock m (Out cl1), Clock m (Out cl2)+  , Clock m (In  cl1), Clock m (In  cl2)+  , GetClockProxy cl1, GetClockProxy cl2+  ) =>+  RhineAndResamplingPoint   m cl1 cl2  a b ->+  Rhine m                         cl2    b c ->+  Rhine m  (SequentialClock m cl1 cl2) a   c RhineAndResamplingPoint (Rhine sn1 cl1) (ResamplingPoint rb cc) --> (Rhine sn2 cl2)  = Rhine (Sequential sn1 rb sn2) (SequentialClock cl1 cl2 cc) @@ -116,10 +127,10 @@  -- | Syntactic sugar for 'RhineParallelAndSchedule'. infix 4 ++@-(++@)-  :: Rhine                    m clL     a b-  -> Schedule                 m clL clR-  -> RhineParallelAndSchedule m clL clR a b+(++@) ::+  Rhine                    m clL     a b ->+  Schedule                 m clL clR     ->+  RhineParallelAndSchedule m clL clR a b (++@) = RhineParallelAndSchedule  {- | The combinators for parallel composition allow for the following syntax:@@ -139,26 +150,26 @@ @ -} infix 3 @++-(@++)-  :: ( Monad m, Clock m clL, Clock m clR-     , Clock m (Out clL), Clock m (Out clR)-     , GetClockProxy clL, GetClockProxy clR-     , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)-     , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)-     , Time clL ~ Time clR-     )-       => RhineParallelAndSchedule m clL clR  a b-       -> Rhine                    m     clR  a c-       -> Rhine m (ParallelClock   m clL clR) a (Either b c)+(@++) ::+  ( Monad m, Clock m clL, Clock m clR+  , Clock m (Out clL), Clock m (Out clR)+  , GetClockProxy clL, GetClockProxy clR+  , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)+  , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)+  , Time clL ~ Time clR+  ) =>+  RhineParallelAndSchedule m clL clR  a         b    ->+  Rhine                    m     clR  a            c ->+  Rhine m (ParallelClock   m clL clR) a (Either b c) RhineParallelAndSchedule (Rhine sn1 clL) schedule @++ (Rhine sn2 clR)   = Rhine (sn1 ++++ sn2) (ParallelClock clL clR schedule)  -- | Further syntactic sugar for 'RhineParallelAndSchedule'. infix 4 ||@-(||@)-  :: Rhine                    m clL     a b-  -> Schedule                 m clL clR-  -> RhineParallelAndSchedule m clL clR a b+(||@) ::+  Rhine                    m clL     a b ->+  Schedule                 m clL clR     ->+  RhineParallelAndSchedule m clL clR a b (||@) = RhineParallelAndSchedule  {- | The combinators for parallel composition allow for the following syntax:@@ -178,53 +189,54 @@ @ -} infix 3 @||-(@||)-  :: ( Monad m, Clock m clL, Clock m clR-     , Clock m (Out clL), Clock m (Out clR)-     , GetClockProxy clL, GetClockProxy clR-     , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)-     , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)-     , Time clL ~ Time clR-     )-       => RhineParallelAndSchedule m clL clR  a b-       -> Rhine                    m     clR  a b-       -> Rhine m (ParallelClock   m clL clR) a b+(@||) ::+  ( Monad m, Clock m clL, Clock m clR+  , Clock m (Out clL), Clock m (Out clR)+  , GetClockProxy clL, GetClockProxy clR+  , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)+  , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)+  , Time clL ~ Time clR+  ) =>+  RhineParallelAndSchedule m clL clR  a b ->+  Rhine                    m     clR  a b ->+  Rhine m (ParallelClock   m clL clR) a b RhineParallelAndSchedule (Rhine sn1 clL) schedule @|| (Rhine sn2 clR)   = Rhine (sn1 |||| sn2) (ParallelClock clL clR schedule)   -- | Postcompose a 'Rhine' with a pure function.-(@>>^)-  :: Monad m-  => Rhine m cl a b-  ->             (b -> c)-  -> Rhine m cl a      c+(@>>^) ::+  Monad m =>+  Rhine m cl a b       ->+              (b -> c) ->+  Rhine m cl a      c Rhine sn cl @>>^ f = Rhine (sn >>>^ f) cl  -- | Precompose a 'Rhine' with a pure function.-(^>>@)-  :: Monad m-  =>           (a -> b)-  -> Rhine m cl      b c-  -> Rhine m cl a      c+(^>>@) ::+  Monad m =>+            (a -> b)  ->+  Rhine m cl      b c ->+  Rhine m cl a      c f ^>>@ Rhine sn cl = Rhine (f ^>>> sn) cl  -- | Postcompose a 'Rhine' with a 'ClSF'.-(@>-^)-  :: ( Clock m (Out cl)-     , Time cl ~ Time (Out cl)-     )-  => Rhine m      cl  a b-  -> ClSF  m (Out cl)   b c-  -> Rhine m      cl  a   c+(@>-^) ::+  ( Clock m (Out cl)+  , Time cl ~ Time (Out cl)+  ) =>+  Rhine m      cl  a b   ->+  ClSF  m (Out cl)   b c ->+  Rhine m      cl  a   c Rhine sn cl @>-^ clsf = Rhine (sn >--^ clsf) cl  -- | Precompose a 'Rhine' with a 'ClSF'.-(^->@)-  :: ( Clock m (In cl)-     , Time cl ~ Time (In cl)-     )-  => ClSF  m (In cl) a b-  -> Rhine m     cl    b c-  -> Rhine m     cl  a   c+(^->@) ::+  ( Clock m (In cl)+  , Time cl ~ Time (In cl)+  ) =>+  ClSF  m (In cl) a b   ->+  Rhine m     cl    b c ->+  Rhine m     cl  a   c clsf ^->@ Rhine sn cl = Rhine (clsf ^--> sn) cl+{- FOURMOLU_ENABLE -}
src/FRP/Rhine/ResamplingBuffer.hs view
@@ -1,3 +1,7 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+ {- | This module introduces 'ResamplingBuffer's, which are primitives that consume and produce data at different rates.@@ -5,15 +9,11 @@ (resampling) buffers form the boundaries between synchronous signal functions ticking at different speeds. -}--{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeFamilies #-}-module FRP.Rhine.ResamplingBuffer-  ( module FRP.Rhine.ResamplingBuffer-  , module FRP.Rhine.Clock-  )-  where+module FRP.Rhine.ResamplingBuffer (+  module FRP.Rhine.ResamplingBuffer,+  module FRP.Rhine.Clock,+)+where  -- rhine import FRP.Rhine.Clock@@ -37,30 +37,30 @@ * 'b': The output type -} data ResamplingBuffer m cla clb a b = ResamplingBuffer-  { put-      :: TimeInfo cla-      -> a-      -> m (   ResamplingBuffer m cla clb a b)-    -- ^ Store one input value of type 'a' at a given time stamp,-    --   and return a continuation.-  , get-      :: TimeInfo clb-      -> m (b, ResamplingBuffer m cla clb a b)-    -- ^ Retrieve one output value of type 'b' at a given time stamp,-    --   and a continuation.+  { put ::+      TimeInfo cla ->+      a ->+      m (ResamplingBuffer m cla clb a b)+  -- ^ Store one input value of type 'a' at a given time stamp,+  --   and return a continuation.+  , get ::+      TimeInfo clb ->+      m (b, ResamplingBuffer m cla clb a b)+  -- ^ Retrieve one output value of type 'b' at a given time stamp,+  --   and a continuation.   }  -- | A type synonym to allow for abbreviation. type ResBuf m cla clb a b = ResamplingBuffer m cla clb a b - -- | Hoist a 'ResamplingBuffer' along a monad morphism.-hoistResamplingBuffer-  :: (Monad m1, Monad m2)-  => (forall c. m1 c -> m2 c)-  -> ResamplingBuffer m1 cla clb a b-  -> ResamplingBuffer m2 cla clb a b-hoistResamplingBuffer hoist ResamplingBuffer {..} = ResamplingBuffer-  { put = (((hoistResamplingBuffer hoist <$>) . hoist) .) . put-  , get = (second (hoistResamplingBuffer hoist) <$>) . hoist . get-  }+hoistResamplingBuffer ::+  (Monad m1, Monad m2) =>+  (forall c. m1 c -> m2 c) ->+  ResamplingBuffer m1 cla clb a b ->+  ResamplingBuffer m2 cla clb a b+hoistResamplingBuffer hoist ResamplingBuffer {..} =+  ResamplingBuffer+    { put = (((hoistResamplingBuffer hoist <$>) . hoist) .) . put+    , get = (second (hoistResamplingBuffer hoist) <$>) . hoist . get+    }
src/FRP/Rhine/ResamplingBuffer/Collect.hs view
@@ -1,10 +1,10 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RecordWildCards #-}+ {- | Resampling buffers that collect the incoming data in some data structure and release all of it on output. -}--{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE RecordWildCards #-} module FRP.Rhine.ResamplingBuffer.Collect where  -- containers@@ -14,42 +14,48 @@ import FRP.Rhine.ResamplingBuffer import FRP.Rhine.ResamplingBuffer.Timeless --- | Collects all input in a list, with the newest element at the head,---   which is returned and emptied upon `get`.+{- | Collects all input in a list, with the newest element at the head,+   which is returned and emptied upon `get`.+-} collect :: Monad m => ResamplingBuffer m cl1 cl2 a [a] collect = timelessResamplingBuffer AsyncMealy {..} []   where     amPut as a = return $ a : as-    amGet as   = return (as, [])-+    amGet as = return (as, []) --- | Reimplementation of 'collect' with sequences,---   which gives a performance benefit if the sequence needs to be reversed or searched.+{- | Reimplementation of 'collect' with sequences,+   which gives a performance benefit if the sequence needs to be reversed or searched.+-} collectSequence :: Monad m => ResamplingBuffer m cl1 cl2 a (Seq a) collectSequence = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ a <| as-    amGet as   = return (as, empty)+    amGet as = return (as, empty) --- | 'pureBuffer' collects all input values lazily in a list---   and processes it when output is required.---   Semantically, @pureBuffer f == collect >>-^ arr f@,---   but 'pureBuffer' is slightly more efficient.+{- | 'pureBuffer' collects all input values lazily in a list+   and processes it when output is required.+   Semantically, @pureBuffer f == collect >>-^ arr f@,+   but 'pureBuffer' is slightly more efficient.+-} pureBuffer :: Monad m => ([a] -> b) -> ResamplingBuffer m cl1 cl2 a b pureBuffer f = timelessResamplingBuffer AsyncMealy {..} []   where     amPut as a = return (a : as)-    amGet as   = return (f as, [])+    amGet as = return (f as, [])  -- TODO Test whether strictness works here, or consider using deepSeq--- | A buffer collecting all incoming values with a folding function.---   It is strict, i.e. the state value 'b' is calculated on every 'put'.-foldBuffer-  :: Monad m-  => (a -> b -> b) -- ^ The folding function-  -> b -- ^ The initial value-  -> ResamplingBuffer m cl1 cl2 a b++{- | A buffer collecting all incoming values with a folding function.+   It is strict, i.e. the state value 'b' is calculated on every 'put'.+-}+foldBuffer ::+  Monad m =>+  -- | The folding function+  (a -> b -> b) ->+  -- | The initial value+  b ->+  ResamplingBuffer m cl1 cl2 a b foldBuffer f = timelessResamplingBuffer AsyncMealy {..}   where     amPut b a = let !b' = f a b in return b'-    amGet b   = return (b, b)+    amGet b = return (b, b)
src/FRP/Rhine/ResamplingBuffer/FIFO.hs view
@@ -1,8 +1,8 @@+{-# LANGUAGE RecordWildCards #-}+ {- | Different implementations of FIFO buffers. -}--{-# LANGUAGE RecordWildCards #-} module FRP.Rhine.ResamplingBuffer.FIFO where  -- base@@ -17,31 +17,33 @@  -- * FIFO (first-in-first-out) buffers --- | An unbounded FIFO buffer.---   If the buffer is empty, it will return 'Nothing'.+{- | An unbounded FIFO buffer.+   If the buffer is empty, it will return 'Nothing'.+-} fifoUnbounded :: Monad m => ResamplingBuffer m cl1 cl2 a (Maybe a) fifoUnbounded = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ a <| as-    amGet as   = case viewr as of-      EmptyR   -> return (Nothing, empty)-      as' :> a -> return (Just a , as'  )+    amGet as = case viewr as of+      EmptyR -> return (Nothing, empty)+      as' :> a -> return (Just a, as') --- |  A bounded FIFO buffer that forgets the oldest values when the size is above a given threshold.---    If the buffer is empty, it will return 'Nothing'.+{- |  A bounded FIFO buffer that forgets the oldest values when the size is above a given threshold.+    If the buffer is empty, it will return 'Nothing'.+-} fifoBounded :: Monad m => Int -> ResamplingBuffer m cl1 cl2 a (Maybe a) fifoBounded threshold = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ take threshold $ a <| as     amGet as = case viewr as of-      EmptyR   -> return (Nothing, empty)-      as' :> a -> return (Just a , as'  )+      EmptyR -> return (Nothing, empty)+      as' :> a -> return (Just a, as')  -- | An unbounded FIFO buffer that also returns its current size. fifoWatch :: Monad m => ResamplingBuffer m cl1 cl2 a (Maybe a, Int) fifoWatch = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ a <| as-    amGet as   = case viewr as of-      EmptyR   -> return ((Nothing, 0         ), empty)-      as' :> a -> return ((Just a , length as'), as'  )+    amGet as = case viewr as of+      EmptyR -> return ((Nothing, 0), empty)+      as' :> a -> return ((Just a, length as'), as')
src/FRP/Rhine/ResamplingBuffer/Interpolation.hs view
@@ -1,11 +1,11 @@-{- |-Interpolation buffers.--}- {-# LANGUAGE Arrows #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-}++{- |+Interpolation buffers.+-} module FRP.Rhine.ResamplingBuffer.Interpolation where  -- containers@@ -17,26 +17,30 @@ -- rhine import FRP.Rhine.ClSF import FRP.Rhine.ResamplingBuffer-import FRP.Rhine.ResamplingBuffer.Util import FRP.Rhine.ResamplingBuffer.KeepLast+import FRP.Rhine.ResamplingBuffer.Util  -- | A simple linear interpolation based on the last calculated position and velocity.-linear-  :: ( Monad m, Clock m cl1, Clock m cl2-     , VectorSpace v s-     , s ~ Diff (Time cl1)-     , s ~ Diff (Time cl2)-     )-  => v -- ^ The initial velocity (derivative of the signal)-  -> v -- ^ The initial position-  -> ResamplingBuffer m cl1 cl2 v v-linear initVelocity initPosition-  =    (derivativeFrom initPosition &&& clId) &&& timeInfoOf sinceInit-  ^->> keepLast ((initVelocity, initPosition), 0)-  >>-^ proc ((velocity, lastPosition), sinceInit1) -> do-    sinceInit2 <- timeInfoOf sinceInit -< ()-    let diff = sinceInit2 - sinceInit1-    returnA -< lastPosition ^+^ diff *^ velocity+linear ::+  ( Monad m+  , Clock m cl1+  , Clock m cl2+  , VectorSpace v s+  , s ~ Diff (Time cl1)+  , s ~ Diff (Time cl2)+  ) =>+  -- | The initial velocity (derivative of the signal)+  v ->+  -- | The initial position+  v ->+  ResamplingBuffer m cl1 cl2 v v+linear initVelocity initPosition =+  (derivativeFrom initPosition &&& clId) &&& timeInfoOf sinceInit+    ^->> keepLast ((initVelocity, initPosition), 0)+      >>-^ proc ((velocity, lastPosition), sinceInit1) -> do+        sinceInit2 <- timeInfoOf sinceInit -< ()+        let diff = sinceInit2 - sinceInit1+        returnA -< lastPosition ^+^ diff *^ velocity  {- | sinc-Interpolation, or Whittaker-Shannon-Interpolation.@@ -49,44 +53,53 @@ the buffer only remembers the past values within a given window, which should be chosen much larger than the average time between @cl1@'s ticks. -}-sinc-  :: ( Monad m, Clock m cl1, Clock m cl2-     , VectorSpace v s-     , Ord (s)-     , Floating (s)-     , s ~ Diff (Time cl1)-     , s ~ Diff (Time cl2)-     )-  => s-  -- ^ The size of the interpolation window+sinc ::+  ( Monad m+  , Clock m cl1+  , Clock m cl2+  , VectorSpace v s+  , Ord s+  , Floating s+  , s ~ Diff (Time cl1)+  , s ~ Diff (Time cl2)+  ) =>+  -- | The size of the interpolation window   --   (for how long in the past to remember incoming values)-  -> ResamplingBuffer m cl1 cl2 v v-sinc windowSize = historySince windowSize ^->> keepLast empty >>-^ proc as -> do-  sinceInit2 <- sinceInitS -< ()-  returnA                  -< vectorSum $ mkSinc sinceInit2 <$> as+  s ->+  ResamplingBuffer m cl1 cl2 v v+sinc windowSize =+  historySince windowSize+    ^->> keepLast empty >>-^ proc as -> do+      sinceInit2 <- sinceInitS -< ()+      returnA -< vectorSum $ mkSinc sinceInit2 <$> as   where-    mkSinc sinceInit2 (TimeInfo {..}, as)-      = let t = pi * (sinceInit2 - sinceInit) / sinceLast-        in  (sin t / t) *^ as+    mkSinc sinceInit2 (TimeInfo {..}, as) =+      let t = pi * (sinceInit2 - sinceInit) / sinceLast+       in (sin t / t) *^ as     vectorSum = foldr (^+^) zeroVector  -- TODO Do we want to give initial values?--- | Interpolates the signal with Hermite splines,---   using 'threePointDerivative'.------   Caution: In order to calculate the derivatives of the incoming signal,---   it has to be delayed by two ticks of @cl1@.---   In a non-realtime situation, a higher quality is achieved---   if the ticks of @cl2@ are delayed by two ticks of @cl1@.-cubic-  :: ( Monad m-     , VectorSpace v s-     , Floating v, Eq v-     , s ~ Diff (Time cl1)-     , s ~ Diff (Time cl2)-     )-  => ResamplingBuffer m cl1 cl2 v v-cubic = ((iPre zeroVector &&& threePointDerivative) &&& (sinceInitS >-> iPre 0))++{- | Interpolates the signal with Hermite splines,+   using 'threePointDerivative'.++   Caution: In order to calculate the derivatives of the incoming signal,+   it has to be delayed by two ticks of @cl1@.+   In a non-realtime situation, a higher quality is achieved+   if the ticks of @cl2@ are delayed by two ticks of @cl1@.+-}+cubic ::+  ( Monad m+  , VectorSpace v s+  , Floating v+  , Eq v+  , s ~ Diff (Time cl1)+  , s ~ Diff (Time cl2)+  ) =>+  ResamplingBuffer m cl1 cl2 v v+{- FOURMOLU_DISABLE -}+cubic =+  ((iPre zeroVector &&& threePointDerivative) &&& (sinceInitS >-> iPre 0))     >-> (clId &&& iPre (zeroVector, 0))    ^->> keepLast ((zeroVector, 0), (zeroVector, 0))    >>-^ proc (((dv, v), t1), ((dv', v'), t1')) -> do@@ -100,3 +113,4 @@               ^+^ (-2 * tcubed + 3 * tsquared        ) *^  v               ^+^ (     tcubed -     tsquared        ) *^ dv      returnA -< vInter+{- FOURMOLU_ENABLE -}
src/FRP/Rhine/ResamplingBuffer/KeepLast.hs view
@@ -1,19 +1,20 @@+{-# LANGUAGE RecordWildCards #-}+ {- | A buffer keeping the last value, or zero-order hold. -}--{-# LANGUAGE RecordWildCards #-} module FRP.Rhine.ResamplingBuffer.KeepLast where  import FRP.Rhine.ResamplingBuffer import FRP.Rhine.ResamplingBuffer.Timeless --- | Always keeps the last input value,---   or in case of no input an initialisation value.---   If @cl2@ approximates continuity,---   this behaves like a zero-order hold.+{- | Always keeps the last input value,+   or in case of no input an initialisation value.+   If @cl2@ approximates continuity,+   this behaves like a zero-order hold.+-} keepLast :: Monad m => a -> ResamplingBuffer m cl1 cl2 a a keepLast = timelessResamplingBuffer AsyncMealy {..}   where-    amPut _ a = return a-    amGet   a = return (a, a)+    amGet a = return (a, a)+    amPut _ = return
src/FRP/Rhine/ResamplingBuffer/LIFO.hs view
@@ -1,8 +1,8 @@+{-# LANGUAGE RecordWildCards #-}+ {- | Different implementations of LIFO buffers. -}--{-# LANGUAGE RecordWildCards #-} module FRP.Rhine.ResamplingBuffer.LIFO where  -- base@@ -17,31 +17,33 @@  -- * LIFO (last-in-first-out) buffers --- | An unbounded LIFO buffer.---   If the buffer is empty, it will return 'Nothing'.+{- | An unbounded LIFO buffer.+   If the buffer is empty, it will return 'Nothing'.+-} lifoUnbounded :: Monad m => ResamplingBuffer m cl1 cl2 a (Maybe a) lifoUnbounded = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ a <| as-    amGet as   = case viewl as of-      EmptyL   -> return (Nothing, empty)-      a :< as' -> return (Just a , as'  )+    amGet as = case viewl as of+      EmptyL -> return (Nothing, empty)+      a :< as' -> return (Just a, as') --- |  A bounded LIFO buffer that forgets the oldest values when the size is above a given threshold.---   If the buffer is empty, it will return 'Nothing'.+{- |  A bounded LIFO buffer that forgets the oldest values when the size is above a given threshold.+   If the buffer is empty, it will return 'Nothing'.+-} lifoBounded :: Monad m => Int -> ResamplingBuffer m cl1 cl2 a (Maybe a) lifoBounded threshold = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ take threshold $ a <| as     amGet as = case viewl as of-      EmptyL   -> return (Nothing, empty)-      a :< as' -> return (Just a , as'  )+      EmptyL -> return (Nothing, empty)+      a :< as' -> return (Just a, as')  -- | An unbounded LIFO buffer that also returns its current size. lifoWatch :: Monad m => ResamplingBuffer m cl1 cl2 a (Maybe a, Int) lifoWatch = timelessResamplingBuffer AsyncMealy {..} empty   where     amPut as a = return $ a <| as-    amGet as   = case viewl as of-      EmptyL   -> return ((Nothing, 0         ), empty)-      a :< as' -> return ((Just a , length as'), as'  )+    amGet as = case viewl as of+      EmptyL -> return ((Nothing, 0), empty)+      a :< as' -> return ((Just a, length as'), as')
src/FRP/Rhine/ResamplingBuffer/MSF.hs view
@@ -1,8 +1,8 @@+{-# LANGUAGE RecordWildCards #-}+ {- | Collect and process all incoming values statefully and with time stamps. -}--{-# LANGUAGE RecordWildCards #-} module FRP.Rhine.ResamplingBuffer.MSF where  -- dunai@@ -11,29 +11,30 @@ -- rhine import FRP.Rhine.ResamplingBuffer --- | Given a monadic stream function that accepts---   a varying number of inputs (a list),---   a `ResamplingBuffer` can be formed---   that collects all input in a timestamped list.-msfBuffer-  :: Monad m-  => MSF m (TimeInfo cl2, [(TimeInfo cl1, a)]) b-  -- ^ The monadic stream function that consumes+{- | Given a monadic stream function that accepts+   a varying number of inputs (a list),+   a `ResamplingBuffer` can be formed+   that collects all input in a timestamped list.+-}+msfBuffer ::+  Monad m =>+  -- | The monadic stream function that consumes   --   a single time stamp for the moment when an output value is required,   --   and a list of timestamped inputs,   --   and outputs a single value.   --   The list will contain the /newest/ element in the head.-  -> ResamplingBuffer m cl1 cl2 a b+  MSF m (TimeInfo cl2, [(TimeInfo cl1, a)]) b ->+  ResamplingBuffer m cl1 cl2 a b msfBuffer = msfBuffer' []   where-    msfBuffer'-      :: Monad m-      => [(TimeInfo cl1, a)]-      -> MSF m (TimeInfo cl2, [(TimeInfo cl1, a)]) b-      -> ResamplingBuffer m cl1 cl2 a b+    msfBuffer' ::+      Monad m =>+      [(TimeInfo cl1, a)] ->+      MSF m (TimeInfo cl2, [(TimeInfo cl1, a)]) b ->+      ResamplingBuffer m cl1 cl2 a b     msfBuffer' as msf = ResamplingBuffer {..}       where         put ti1 a = return $ msfBuffer' ((ti1, a) : as) msf-        get ti2   = do+        get ti2 = do           (b, msf') <- unMSF msf (ti2, as)           return (b, msfBuffer msf')
src/FRP/Rhine/ResamplingBuffer/Timeless.hs view
@@ -1,46 +1,57 @@+{-# LANGUAGE RecordWildCards #-}+ {- | Resampling buffers from asynchronous Mealy machines. These are used in many other modules implementing 'ResamplingBuffer's. -}--{-# LANGUAGE RecordWildCards #-} module FRP.Rhine.ResamplingBuffer.Timeless where  import FRP.Rhine.ResamplingBuffer --- | An asynchronous, effectful Mealy machine description.---   (Input and output do not happen simultaneously.)---   It can be used to create 'ResamplingBuffer's.+{- | An asynchronous, effectful Mealy machine description.+   (Input and output do not happen simultaneously.)+   It can be used to create 'ResamplingBuffer's.+-}+{- FOURMOLU_DISABLE -} data AsyncMealy m s a b = AsyncMealy-  { amPut :: s -> a -> m     s -- ^ Given the previous state and an input value, return the new state.-  , amGet :: s      -> m (b, s) -- ^ Given the previous state, return an output value and a new state.+  { amPut :: s -> a -> m     s+  -- ^ Given the previous state and an input value, return the new state.+  , amGet :: s      -> m (b, s)+  -- ^ Given the previous state, return an output value and a new state.   }+{- FOURMOLU_ENABLE -} --- | A resampling buffer that is unaware of the time information of the clock,---   and thus clock-polymorphic.---   It is built from an asynchronous Mealy machine description.---   Whenever 'get' is called on @timelessResamplingBuffer machine s@,---   the method 'amGet' is called on @machine@ with state @s@,---   discarding the time stamp. Analogously for 'put'.-timelessResamplingBuffer-  :: Monad m-  => AsyncMealy m s a b -- The asynchronous Mealy machine from which the buffer is built-  -> s -- ^ The initial state-  -> ResamplingBuffer m cl1 cl2 a b+{- | A resampling buffer that is unaware of the time information of the clock,+   and thus clock-polymorphic.+   It is built from an asynchronous Mealy machine description.+   Whenever 'get' is called on @timelessResamplingBuffer machine s@,+   the method 'amGet' is called on @machine@ with state @s@,+   discarding the time stamp. Analogously for 'put'.+-}+timelessResamplingBuffer ::+  Monad m =>+  AsyncMealy m s a b -> -- The asynchronous Mealy machine from which the buffer is built++  -- | The initial state+  s ->+  ResamplingBuffer m cl1 cl2 a b timelessResamplingBuffer AsyncMealy {..} = go   where     go s =       let         put _ a = go <$> amPut s a-        get _   = do+        get _ = do           (b, s') <- amGet s           return (b, go s')-      in ResamplingBuffer {..}+       in+        ResamplingBuffer {..}  -- | A resampling buffer that only accepts and emits units. trivialResamplingBuffer :: Monad m => ResamplingBuffer m cl1 cl2 () ()-trivialResamplingBuffer = timelessResamplingBuffer AsyncMealy-  { amPut = const (const (return ()))-  , amGet = const (return ((), ()))-  }-  ()+trivialResamplingBuffer =+  timelessResamplingBuffer+    AsyncMealy+      { amPut = const (const (return ()))+      , amGet = const (return ((), ()))+      }+    ()
src/FRP/Rhine/ResamplingBuffer/Util.hs view
@@ -1,8 +1,8 @@+{-# LANGUAGE RankNTypes #-}+ {- | Several utilities to create 'ResamplingBuffer's. -}--{-# LANGUAGE RankNTypes #-} module FRP.Rhine.ResamplingBuffer.Util where  -- transformers@@ -12,73 +12,81 @@ import Data.MonadicStreamFunction.InternalCore  -- rhine-import FRP.Rhine.Clock import FRP.Rhine.ClSF+import FRP.Rhine.Clock import FRP.Rhine.ResamplingBuffer  -- * Utilities to build 'ResamplingBuffer's from smaller components  infix 2 >>-^++{- FOURMOLU_DISABLE -}+ -- | Postcompose a 'ResamplingBuffer' with a matching 'ClSF'.-(>>-^) :: Monad m-      => ResamplingBuffer m cl1 cl2 a b-      -> ClSF             m     cl2   b c-      -> ResamplingBuffer m cl1 cl2 a   c+(>>-^) ::+  Monad m =>+  ResamplingBuffer m cl1 cl2 a b   ->+  ClSF             m     cl2   b c ->+  ResamplingBuffer m cl1 cl2 a   c resBuf >>-^ clsf = ResamplingBuffer put_ get_   where     put_ theTimeInfo a = (>>-^ clsf) <$> put resBuf theTimeInfo a-    get_ theTimeInfo   = do+    get_ theTimeInfo = do       (b, resBuf') <- get resBuf theTimeInfo-      (c, clsf')   <- unMSF clsf b `runReaderT` theTimeInfo+      (c, clsf') <- unMSF clsf b `runReaderT` theTimeInfo       return (c, resBuf' >>-^ clsf') - infix 1 ^->>+ -- | Precompose a 'ResamplingBuffer' with a matching 'ClSF'.-(^->>) :: Monad m-      => ClSF             m cl1     a b-      -> ResamplingBuffer m cl1 cl2   b c-      -> ResamplingBuffer m cl1 cl2 a   c+(^->>) ::+  Monad m =>+  ClSF             m cl1     a b   ->+  ResamplingBuffer m cl1 cl2   b c ->+  ResamplingBuffer m cl1 cl2 a   c clsf ^->> resBuf = ResamplingBuffer put_ get_   where     put_ theTimeInfo a = do       (b, clsf') <- unMSF clsf a `runReaderT` theTimeInfo-      resBuf'    <- put resBuf theTimeInfo b+      resBuf' <- put resBuf theTimeInfo b       return $ clsf' ^->> resBuf'-    get_ theTimeInfo   = second (clsf ^->>) <$> get resBuf theTimeInfo-+    get_ theTimeInfo = second (clsf ^->>) <$> get resBuf theTimeInfo  infixl 4 *-*+ -- | Parallely compose two 'ResamplingBuffer's.-(*-*) :: Monad m-      => ResamplingBuffer m cl1 cl2  a      b-      -> ResamplingBuffer m cl1 cl2     c      d-      -> ResamplingBuffer m cl1 cl2 (a, c) (b, d)+(*-*) ::+  Monad m =>+  ResamplingBuffer m cl1 cl2  a      b    ->+  ResamplingBuffer m cl1 cl2     c      d ->+  ResamplingBuffer m cl1 cl2 (a, c) (b, d) resBuf1 *-* resBuf2 = ResamplingBuffer put_ get_   where     put_ theTimeInfo (a, c) = do       resBuf1' <- put resBuf1 theTimeInfo a       resBuf2' <- put resBuf2 theTimeInfo c       return $ resBuf1' *-* resBuf2'-    get_ theTimeInfo        = do+    get_ theTimeInfo = do       (b, resBuf1') <- get resBuf1 theTimeInfo       (d, resBuf2') <- get resBuf2 theTimeInfo       return ((b, d), resBuf1' *-* resBuf2')  infixl 4 &-&+ -- | Parallely compose two 'ResamplingBuffer's, duplicating the input.-(&-&) :: Monad m-      => ResamplingBuffer m cl1 cl2  a  b-      -> ResamplingBuffer m cl1 cl2  a     c-      -> ResamplingBuffer m cl1 cl2  a (b, c)+(&-&) ::+  Monad m =>+  ResamplingBuffer m cl1 cl2  a  b    ->+  ResamplingBuffer m cl1 cl2  a     c ->+  ResamplingBuffer m cl1 cl2  a (b, c) resBuf1 &-& resBuf2 = arr (\a -> (a, a)) ^->> resBuf1 *-* resBuf2 ---- | Given a 'ResamplingBuffer' where the output type depends on the input type polymorphically,---   we can produce a timestamped version that simply annotates every input value---   with the 'TimeInfo' when it arrived.-timestamped-  :: Monad m-  => (forall b. ResamplingBuffer m cl clf b (f b))-  -> ResamplingBuffer m cl clf a (f (a, TimeInfo cl))+{- | Given a 'ResamplingBuffer' where the output type depends on the input type polymorphically,+   we can produce a timestamped version that simply annotates every input value+   with the 'TimeInfo' when it arrived.+-}+timestamped ::+  Monad m =>+  (forall b. ResamplingBuffer m cl clf b (f b)) ->+  ResamplingBuffer m cl clf a (f (a, TimeInfo cl)) timestamped resBuf = (clId &&& timeInfo) ^->> resBuf
src/FRP/Rhine/SN.hs view
@@ -1,3 +1,8 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+ {- | Asynchronous signal networks are combinations of clocked signal functions ('ClSF's) and matching 'ResamplingBuffer's,@@ -6,21 +11,16 @@ This module defines the 'SN' type, combinators are found in a submodule. -}--{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.SN where - -- rhine+import FRP.Rhine.ClSF.Core import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy-import FRP.Rhine.ClSF.Core import FRP.Rhine.ResamplingBuffer import FRP.Rhine.Schedule +{- FOURMOLU_DISABLE -}  {- | An 'SN' is a side-effectful asynchronous /__s__ignal __n__etwork/, where input, data processing (including side effects) and output@@ -37,73 +37,79 @@ data SN m cl a b where   -- | A synchronous monadic stream function is the basic building block.   --   For such an 'SN', data enters and leaves the system at the same rate as it is processed.-  Synchronous-    :: ( cl ~ In cl, cl ~ Out cl)-    => ClSF m cl a b-    -> SN   m cl a b+  Synchronous ::+    ( cl ~ In cl, cl ~ Out cl) =>+    ClSF m cl a b ->+    SN   m cl a b+   -- | Two 'SN's may be sequentially composed if there is a matching 'ResamplingBuffer' between them.-  Sequential-    :: ( Clock m clab, Clock m clcd-       , Clock m (Out clab), Clock m (Out clcd)-       , Clock m (In  clab), Clock m (In  clcd)-       , GetClockProxy clab, GetClockProxy clcd-       , Time clab ~ Time clcd-       , Time clab ~ Time (Out clab)-       , Time clcd ~ Time (In  clcd)-       )-    => SN               m      clab            a b-    -> ResamplingBuffer m (Out clab) (In clcd)   b c-    -> SN               m                clcd      c d-    -> SN m (SequentialClock m clab      clcd) a     d+  Sequential ::+    ( Clock m clab, Clock m clcd+    , Clock m (Out clab), Clock m (Out clcd)+    , Clock m (In  clab), Clock m (In  clcd)+    , GetClockProxy clab, GetClockProxy clcd+    , Time clab ~ Time clcd+    , Time clab ~ Time (Out clab)+    , Time clcd ~ Time (In  clcd)+    ) =>+    SN               m      clab            a b     ->+    ResamplingBuffer m (Out clab) (In clcd)   b c   ->+    SN               m                clcd      c d ->+    SN m (SequentialClock m clab      clcd) a     d+   -- | Two 'SN's with the same input and output data may be parallely composed.-  Parallel-    :: ( Clock m cl1, Clock m cl2-       , Clock m (Out cl1), Clock m (Out cl2)-       , GetClockProxy cl1, GetClockProxy cl2-       , Time cl1 ~ Time (Out cl1)-       , Time cl2 ~ Time (Out cl2)-       , Time cl1 ~ Time cl2-       , Time cl1 ~ Time (In cl1)-       , Time cl2 ~ Time (In cl2)-       )-    => SN m                  cl1      a b-    -> SN m                      cl2  a b-    -> SN m (ParallelClock m cl1 cl2) a b+  Parallel ::+    ( Clock m cl1, Clock m cl2+    , Clock m (Out cl1), Clock m (Out cl2)+    , GetClockProxy cl1, GetClockProxy cl2+    , Time cl1 ~ Time (Out cl1)+    , Time cl2 ~ Time (Out cl2)+    , Time cl1 ~ Time cl2+    , Time cl1 ~ Time (In cl1)+    , Time cl2 ~ Time (In cl2)+    ) =>+    SN m                  cl1      a b ->+    SN m                      cl2  a b ->+    SN m (ParallelClock m cl1 cl2) a b+   -- | Bypass the signal network by forwarding data in parallel through a 'ResamplingBuffer'.-  FirstResampling-    :: ( Clock m (In cl), Clock m (Out cl)-       , Time cl ~ Time (Out cl)-       , Time cl ~ Time (In cl)-       )-    => SN               m cl               a      b-    -> ResamplingBuffer m (In cl) (Out cl)    c      d-    -> SN               m cl              (a, c) (b, d)+  FirstResampling ::+    ( Clock m (In cl), Clock m (Out cl)+    , Time cl ~ Time (Out cl)+    , Time cl ~ Time (In cl)+    ) =>+    SN               m cl               a      b    ->+    ResamplingBuffer m (In cl) (Out cl)    c      d ->+    SN               m cl              (a, c) (b, d)+   -- | A 'ClSF' can always be postcomposed onto an 'SN' if the clocks match on the output.-  Postcompose-    :: ( Clock m (Out cl)-       , Time cl ~ Time (Out cl)-       )-    => SN    m      cl  a b-    -> ClSF  m (Out cl)   b c-    -> SN    m      cl  a   c+  Postcompose ::+    ( Clock m (Out cl)+    , Time cl ~ Time (Out cl)+    ) =>+    SN    m      cl  a b   ->+    ClSF  m (Out cl)   b c ->+    SN    m      cl  a   c+   -- | A 'ClSF' can always be precomposed onto an 'SN' if the clocks match on the input.-  Precompose-    :: ( Clock m (In cl)-       , Time cl ~ Time (In cl)-       )-    => ClSF m (In cl) a b-    -> SN   m     cl    b c-    -> SN   m     cl  a   c+  Precompose ::+    ( Clock m (In cl)+    , Time cl ~ Time (In cl)+    ) =>+    ClSF m (In cl) a b   ->+    SN   m     cl    b c ->+    SN   m     cl  a   c+   -- | Data can be looped back to the beginning of an 'SN',   --   but it must be resampled since the 'Out' and 'In' clocks are generally different.-  Feedback-    :: ( Clock m (In cl),  Clock m (Out cl)-       , Time (In cl) ~ Time cl-       , Time (Out cl) ~ Time cl-       )-    => ResBuf m (Out cl) (In cl) d c-    -> SN     m cl (a, c) (b, d)-    -> SN     m cl  a      b+  Feedback ::+    ( Clock m (In cl),  Clock m (Out cl)+    , Time (In cl) ~ Time cl+    , Time (Out cl) ~ Time cl+    ) =>+    ResBuf m (Out cl) (In cl) d c ->+    SN     m cl (a, c) (b, d) ->+    SN     m cl  a      b  instance GetClockProxy cl => ToClockProxy (SN m cl a b) where   type Cl (SN m cl a b) = cl
src/FRP/Rhine/SN/Combinators.hs view
@@ -1,20 +1,20 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+ {- | Combinators for composing signal networks sequentially and parallely. -}--{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-} module FRP.Rhine.SN.Combinators where - -- rhine import FRP.Rhine.ClSF.Core+import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy import FRP.Rhine.ResamplingBuffer.Util-import FRP.Rhine.Schedule import FRP.Rhine.SN-+import FRP.Rhine.Schedule +{- FOURMOLU_DISABLE -} -- | Postcompose a signal network with a pure function. (>>>^)   :: Monad m@@ -75,21 +75,17 @@   where     sn1 = sn11 **** sn21     sn2 = sn12 **** sn22-    rb  = rb1 *-* rb2-Parallel sn11 sn12 **** Parallel sn21 sn22-  = Parallel (sn11 **** sn21) (sn12 **** sn22)-+    rb = rb1 *-* rb2+Parallel sn11 sn12 **** Parallel sn21 sn22 =+  Parallel (sn11 **** sn21) (sn12 **** sn22) Precompose clsf sn1 **** sn2 = Precompose (first clsf) $ sn1 **** sn2 sn1 **** Precompose clsf sn2 = Precompose (second clsf) $ sn1 **** sn2 Postcompose sn1 clsf **** sn2 = Postcompose (sn1 **** sn2) (first clsf) sn1 **** Postcompose sn2 clsf = Postcompose (sn1 **** sn2) (second clsf)- Feedback buf sn1 **** sn2 = Feedback buf $ (\((a, c), c1) -> ((a, c1), c)) ^>>> (sn1 **** sn2) >>>^ (\((b, d1), d) -> ((b, d), d1)) sn1 **** Feedback buf sn2 = Feedback buf $ (\((a, c), c1) -> (a, (c, c1))) ^>>> (sn1 **** sn2) >>>^ (\(b, (d, d1)) -> ((b, d), d1))- FirstResampling sn1 buf **** sn2 = (\((a1, c1), c) -> ((a1, c), c1)) ^>>> FirstResampling (sn1 **** sn2) buf >>>^ (\((b1, d), d1) -> ((b1, d1), d)) sn1 **** FirstResampling sn2 buf = (\(a, (a1, c1)) -> ((a, a1), c1)) ^>>> FirstResampling (sn1 **** sn2) buf >>>^ (\((b, b1), d1) -> (b, (b1, d1)))- -- Note that the patterns above are the only ones that can occur. -- This is ensured by the clock constraints in the SF constructors. Synchronous _ **** Parallel _ _ = error "Impossible pattern: Synchronous _ **** Parallel _ _"
src/FRP/Rhine/Schedule.hs view
@@ -1,3 +1,12 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+ {- | 'Schedule's are the compatibility mechanism between two different clocks. A schedule' implements the the universal clocks such that those two given clocks@@ -9,16 +18,6 @@  Specific implementations of schedules are found in submodules. -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeFamilies #-}- module FRP.Rhine.Schedule where  -- transformers@@ -33,250 +32,266 @@  -- * The schedule type --- | A schedule implements a combination of two clocks.---   It outputs a time stamp and an 'Either' value,---   which specifies which of the two subclocks has ticked.-data Schedule m cl1 cl2-  = (Time cl1 ~ Time cl2)-  => Schedule-    { initSchedule-        :: cl1 -> cl2-        -> RunningClockInit m (Time cl1) (Either (Tag cl1) (Tag cl2))-    }+{- | A schedule implements a combination of two clocks.+   It outputs a time stamp and an 'Either' value,+   which specifies which of the two subclocks has ticked.+-}+data Schedule m cl1 cl2 = (Time cl1 ~ Time cl2) =>+  Schedule+  { initSchedule ::+      cl1 ->+      cl2 ->+      RunningClockInit m (Time cl1) (Either (Tag cl1) (Tag cl2))+  }+ -- The type constraint in the constructor is actually useful when pattern matching on 'Schedule', -- which is interesting since a constraint like 'Monad m' is useful. -- When reformulating as a GADT, it might get used, -- but that would mean that we can't use record syntax. - -- * Utilities to create new schedules from existing ones  -- | Lift a schedule along a monad morphism.-hoistSchedule-  :: (Monad m1, Monad m2)-  => (forall a . m1 a -> m2 a)-  -> Schedule m1 cl1 cl2-  -> Schedule m2 cl1 cl2+hoistSchedule ::+  (Monad m1, Monad m2) =>+  (forall a. m1 a -> m2 a) ->+  Schedule m1 cl1 cl2 ->+  Schedule m2 cl1 cl2 hoistSchedule hoist Schedule {..} = Schedule initSchedule'   where-    initSchedule' cl1 cl2 = hoist-      $ first (hoistMSF hoist) <$> initSchedule cl1 cl2-    hoistMSF = morphS+    initSchedule' cl1 cl2 =+      hoist $+        first (hoistMSF hoist) <$> initSchedule cl1 cl2     -- TODO This should be a dunai issue+    hoistMSF = morphS  -- | Swaps the clocks for a given schedule.-flipSchedule-  :: Monad m-  => Schedule m cl1 cl2-  -> Schedule m cl2 cl1+flipSchedule ::+  Monad m =>+  Schedule m cl1 cl2 ->+  Schedule m cl2 cl1 flipSchedule Schedule {..} = Schedule initSchedule_   where     initSchedule_ cl2 cl1 = first (arr (second swapEither) <<<) <$> initSchedule cl1 cl2  -- TODO I originally wanted to rescale a schedule and its clocks at the same time. -- That's rescaleSequentialClock.--- | If a schedule works for two clocks, a rescaling of the clocks---   also applies to the schedule.-rescaledSchedule-  :: Monad m-  => Schedule m cl1 cl2-  -> Schedule m (RescaledClock cl1 time) (RescaledClock cl2 time)++{- | If a schedule works for two clocks, a rescaling of the clocks+   also applies to the schedule.+-}+rescaledSchedule ::+  Monad m =>+  Schedule m cl1 cl2 ->+  Schedule m (RescaledClock cl1 time) (RescaledClock cl2 time) rescaledSchedule schedule = Schedule initSchedule'   where     initSchedule' cl1 cl2 = initSchedule (rescaledScheduleS schedule) (rescaledClockToS cl1) (rescaledClockToS cl2)  -- | As 'rescaledSchedule', with a stateful rescaling-rescaledScheduleS-  :: Monad m-  => Schedule m cl1 cl2-  -> Schedule m (RescaledClockS m cl1 time tag1) (RescaledClockS m cl2 time tag2)+rescaledScheduleS ::+  Monad m =>+  Schedule m cl1 cl2 ->+  Schedule m (RescaledClockS m cl1 time tag1) (RescaledClockS m cl2 time tag2) rescaledScheduleS Schedule {..} = Schedule initSchedule'   where     initSchedule' (RescaledClockS cl1 rescaleS1) (RescaledClockS cl2 rescaleS2) = do-      (runningSchedule, initTime ) <- initSchedule cl1 cl2-      (rescaling1     , initTime') <- rescaleS1 initTime-      (rescaling2     , _        ) <- rescaleS2 initTime-      let runningSchedule'-            = runningSchedule >>> proc (time, tag12) -> case tag12 of-                Left  tag1 -> do-                  (time', tag1') <- rescaling1 -< (time, tag1)-                  returnA -< (time', Left  tag1')-                Right tag2 -> do-                  (time', tag2') <- rescaling2 -< (time, tag2)-                  returnA -< (time', Right tag2')+      (runningSchedule, initTime) <- initSchedule cl1 cl2+      (rescaling1, initTime') <- rescaleS1 initTime+      (rescaling2, _) <- rescaleS2 initTime+      let runningSchedule' =+            runningSchedule >>> proc (time, tag12) -> case tag12 of+              Left tag1 -> do+                (time', tag1') <- rescaling1 -< (time, tag1)+                returnA -< (time', Left tag1')+              Right tag2 -> do+                (time', tag2') <- rescaling2 -< (time, tag2)+                returnA -< (time', Right tag2')       return (runningSchedule', initTime') -- -- TODO What's the most general way we can lift a schedule this way?--- | Lifts a schedule into the 'ReaderT' transformer,---   supplying the same environment to its scheduled clocks.-readerSchedule-  :: ( Monad m-     , Clock (ReaderT r m) cl1, Clock (ReaderT r m) cl2-     , Time cl1 ~ Time cl2-     )-  => Schedule m-       (HoistClock (ReaderT r m) m cl1) (HoistClock (ReaderT r m) m cl2)-  -> Schedule (ReaderT r m) cl1 cl2-readerSchedule Schedule {..}-  = Schedule $ \cl1 cl2 -> ReaderT $ \r -> first liftTransS-  <$> initSchedule++{- | Lifts a schedule into the 'ReaderT' transformer,+   supplying the same environment to its scheduled clocks.+-}+readerSchedule ::+  ( Monad m+  , Clock (ReaderT r m) cl1+  , Clock (ReaderT r m) cl2+  , Time cl1 ~ Time cl2+  ) =>+  Schedule+    m+    (HoistClock (ReaderT r m) m cl1)+    (HoistClock (ReaderT r m) m cl2) ->+  Schedule (ReaderT r m) cl1 cl2+readerSchedule Schedule {..} =+  Schedule $ \cl1 cl2 -> ReaderT $ \r ->+    first liftTransS+      <$> initSchedule         (HoistClock cl1 $ flip runReaderT r)         (HoistClock cl2 $ flip runReaderT r) - -- * Composite clocks  -- ** Sequentially combined clocks --- | Two clocks can be combined with a schedule as a clock---   for an asynchronous sequential composition of signal networks.-data SequentialClock m cl1 cl2-  = Time cl1 ~ Time cl2-  => SequentialClock-    { sequentialCl1      :: cl1-    , sequentialCl2      :: cl2-    , sequentialSchedule :: Schedule m cl1 cl2-    }+{- | Two clocks can be combined with a schedule as a clock+   for an asynchronous sequential composition of signal networks.+-}+data SequentialClock m cl1 cl2 = Time cl1 ~ Time cl2 =>+  SequentialClock+  { sequentialCl1 :: cl1+  , sequentialCl2 :: cl2+  , sequentialSchedule :: Schedule m cl1 cl2+  }  -- | Abbrevation synonym. type SeqClock m cl1 cl2 = SequentialClock m cl1 cl2 -instance (Monad m, Clock m cl1, Clock m cl2)-      => Clock m (SequentialClock m cl1 cl2) where+instance+  (Monad m, Clock m cl1, Clock m cl2) =>+  Clock m (SequentialClock m cl1 cl2)+  where   type Time (SequentialClock m cl1 cl2) = Time cl1-  type Tag  (SequentialClock m cl1 cl2) = Either (Tag cl1) (Tag cl2)-  initClock SequentialClock {..}-    = initSchedule sequentialSchedule sequentialCl1 sequentialCl2+  type Tag (SequentialClock m cl1 cl2) = Either (Tag cl1) (Tag cl2)+  initClock SequentialClock {..} =+    initSchedule sequentialSchedule sequentialCl1 sequentialCl2 --- | @cl1@ is a subclock of @SequentialClock m cl1 cl2@,---   therefore it is always possible to schedule these two clocks deterministically.---   The left subclock of the combined clock always ticks instantly after @cl1@.+{- | @cl1@ is a subclock of @SequentialClock m cl1 cl2@,+   therefore it is always possible to schedule these two clocks deterministically.+   The left subclock of the combined clock always ticks instantly after @cl1@.+-} schedSeq1 :: (Monad m, Semigroup cl1) => Schedule m cl1 (SequentialClock m cl1 cl2)-schedSeq1 = Schedule $ \cl1 SequentialClock { sequentialSchedule = Schedule {..}, .. } -> do+schedSeq1 = Schedule $ \cl1 SequentialClock {sequentialSchedule = Schedule {..}, ..} -> do   (runningClock, initTime) <- initSchedule (cl1 <> sequentialCl1) sequentialCl2   return (duplicateSubtick runningClock, initTime) --- | As 'schedSeq1', but for the right subclock.---   The right subclock of the combined clock always ticks instantly before @cl2@.+{- | As 'schedSeq1', but for the right subclock.+   The right subclock of the combined clock always ticks instantly before @cl2@.+-} schedSeq2 :: (Monad m, Semigroup cl2, Time cl1 ~ Time cl2) => Schedule m (SequentialClock m cl1 cl2) cl2-schedSeq2 = Schedule $ \SequentialClock { sequentialSchedule = Schedule {..}, .. } cl2 -> do+schedSeq2 = Schedule $ \SequentialClock {sequentialSchedule = Schedule {..}, ..} cl2 -> do   (runningClock, initTime) <- initSchedule sequentialCl1 (sequentialCl2 <> cl2)   return (duplicateSubtick (runningClock >>> second (arr swapEither)) >>> second (arr remap), initTime)-    where-      remap (Left tag2)          = Left $ Right tag2-      remap (Right (Left tag2))  = Right tag2-      remap (Right (Right tag1)) = Left $ Left tag1+  where+    remap (Left tag2) = Left $ Right tag2+    remap (Right (Left tag2)) = Right tag2+    remap (Right (Right tag1)) = Left $ Left tag1+ -- TODO Why did I need the constraint on the time domains here, but not in schedSeq1? --      Same for schedPar2 - -- ** Parallelly combined clocks ---- | Two clocks can be combined with a schedule as a clock---   for an asynchronous parallel composition of signal networks.-data ParallelClock m cl1 cl2-  = Time cl1 ~ Time cl2-  => ParallelClock-    { parallelCl1      :: cl1-    , parallelCl2      :: cl2-    , parallelSchedule :: Schedule m cl1 cl2-    }+{- | Two clocks can be combined with a schedule as a clock+   for an asynchronous parallel composition of signal networks.+-}+data ParallelClock m cl1 cl2 = Time cl1 ~ Time cl2 =>+  ParallelClock+  { parallelCl1 :: cl1+  , parallelCl2 :: cl2+  , parallelSchedule :: Schedule m cl1 cl2+  }  -- | Abbrevation synonym. type ParClock m cl1 cl2 = ParallelClock m cl1 cl2 -instance (Monad m, Clock m cl1, Clock m cl2)-      => Clock m (ParallelClock m cl1 cl2) where+instance+  (Monad m, Clock m cl1, Clock m cl2) =>+  Clock m (ParallelClock m cl1 cl2)+  where   type Time (ParallelClock m cl1 cl2) = Time cl1-  type Tag  (ParallelClock m cl1 cl2) = Either (Tag cl1) (Tag cl2)-  initClock ParallelClock {..}-    = initSchedule parallelSchedule parallelCl1 parallelCl2-+  type Tag (ParallelClock m cl1 cl2) = Either (Tag cl1) (Tag cl2)+  initClock ParallelClock {..} =+    initSchedule parallelSchedule parallelCl1 parallelCl2 --- | Like 'schedSeq1', but for parallel clocks.---   The left subclock of the combined clock always ticks instantly after @cl1@.+{- | Like 'schedSeq1', but for parallel clocks.+   The left subclock of the combined clock always ticks instantly after @cl1@.+-} schedPar1 :: (Monad m, Semigroup cl1) => Schedule m cl1 (ParallelClock m cl1 cl2)-schedPar1 = Schedule $ \cl1 ParallelClock { parallelSchedule = Schedule {..}, .. } -> do+schedPar1 = Schedule $ \cl1 ParallelClock {parallelSchedule = Schedule {..}, ..} -> do   (runningClock, initTime) <- initSchedule (cl1 <> parallelCl1) parallelCl2   return (duplicateSubtick runningClock, initTime) --- | Like 'schedPar1',---   but the left subclock of the combined clock always ticks instantly /before/ @cl1@.+{- | Like 'schedPar1',+   but the left subclock of the combined clock always ticks instantly /before/ @cl1@.+-} schedPar1' :: (Monad m, Semigroup cl1) => Schedule m cl1 (ParallelClock m cl1 cl2)-schedPar1' = Schedule $ \cl1 ParallelClock { parallelSchedule = Schedule {..}, .. } -> do+schedPar1' = Schedule $ \cl1 ParallelClock {parallelSchedule = Schedule {..}, ..} -> do   (runningClock, initTime) <- initSchedule (parallelCl1 <> cl1) parallelCl2   return (duplicateSubtick runningClock >>> arr (second remap), initTime)-    where-      remap (Left tag1)         = Right $ Left tag1-      remap (Right (Left tag1)) = Left tag1-      remap tag                 = tag+  where+    remap (Left tag1) = Right $ Left tag1+    remap (Right (Left tag1)) = Left tag1+    remap tag = tag --- | Like 'schedPar1', but for the right subclock.---   The right subclock of the combined clock always ticks instantly before @cl2@.+{- | Like 'schedPar1', but for the right subclock.+   The right subclock of the combined clock always ticks instantly before @cl2@.+-} schedPar2 :: (Monad m, Semigroup cl2, Time cl1 ~ Time cl2) => Schedule m (ParallelClock m cl1 cl2) cl2-schedPar2 = Schedule $ \ParallelClock { parallelSchedule = Schedule {..}, .. } cl2 -> do+schedPar2 = Schedule $ \ParallelClock {parallelSchedule = Schedule {..}, ..} cl2 -> do   (runningClock, initTime) <- initSchedule parallelCl1 (parallelCl2 <> cl2)   return (duplicateSubtick (runningClock >>> second (arr swapEither)) >>> second (arr remap), initTime)-    where-      remap (Left tag2)          = Left $ Right tag2-      remap (Right (Left tag2))  = Right tag2-      remap (Right (Right tag1)) = Left $ Left tag1+  where+    remap (Left tag2) = Left $ Right tag2+    remap (Right (Left tag2)) = Right tag2+    remap (Right (Right tag1)) = Left $ Left tag1 --- | Like 'schedPar1',---   but the right subclock of the combined clock always ticks instantly /after/ @cl2@.+{- | Like 'schedPar1',+   but the right subclock of the combined clock always ticks instantly /after/ @cl2@.+-} schedPar2' :: (Monad m, Semigroup cl2, Time cl1 ~ Time cl2) => Schedule m (ParallelClock m cl1 cl2) cl2-schedPar2' = Schedule $ \ParallelClock { parallelSchedule = Schedule {..}, .. } cl2 -> do+schedPar2' = Schedule $ \ParallelClock {parallelSchedule = Schedule {..}, ..} cl2 -> do   (runningClock, initTime) <- initSchedule parallelCl1 (parallelCl2 <> cl2)   return (duplicateSubtick (runningClock >>> second (arr swapEither)) >>> second (arr remap), initTime)-    where-      remap (Left tag2)          = Right tag2-      remap (Right (Left tag2))  = Left $ Right tag2-      remap (Right (Right tag1)) = Left $ Left tag1-+  where+    remap (Left tag2) = Right tag2+    remap (Right (Left tag2)) = Left $ Right tag2+    remap (Right (Right tag1)) = Left $ Left tag1  -- * Navigating the clock tree  -- | The clock that represents the rate at which data enters the system. type family In cl where   In (SequentialClock m cl1 cl2) = In cl1-  In (ParallelClock   m cl1 cl2) = ParallelClock m (In cl1) (In cl2)-  In cl                          = cl+  In (ParallelClock m cl1 cl2) = ParallelClock m (In cl1) (In cl2)+  In cl = cl  -- | The clock that represents the rate at which data leaves the system. type family Out cl where   Out (SequentialClock m cl1 cl2) = Out cl2-  Out (ParallelClock   m cl1 cl2) = ParallelClock m (Out cl1) (Out cl2)-  Out cl                          = cl-+  Out (ParallelClock m cl1 cl2) = ParallelClock m (Out cl1) (Out cl2)+  Out cl = cl --- | A tree representing possible last times to which---   the constituents of a clock may have ticked.+{- | A tree representing possible last times to which+   the constituents of a clock may have ticked.+-} data LastTime cl where-  SequentialLastTime-    :: LastTime cl1 -> LastTime cl2-    -> LastTime (SequentialClock m cl1 cl2)-  ParallelLastTime-    :: LastTime cl1 -> LastTime cl2-    -> LastTime (ParallelClock   m cl1 cl2)+  SequentialLastTime ::+    LastTime cl1 ->+    LastTime cl2 ->+    LastTime (SequentialClock m cl1 cl2)+  ParallelLastTime ::+    LastTime cl1 ->+    LastTime cl2 ->+    LastTime (ParallelClock m cl1 cl2)   LeafLastTime :: Time cl -> LastTime cl - -- | An inclusion of a clock into a tree of parallel compositions of clocks. data ParClockInclusion clS cl where-  ParClockInL-    :: ParClockInclusion (ParallelClock m clL clR) cl-    -> ParClockInclusion                  clL      cl-  ParClockInR-    :: ParClockInclusion (ParallelClock m clL clR) cl-    -> ParClockInclusion                      clR  cl+  ParClockInL ::+    ParClockInclusion (ParallelClock m clL clR) cl ->+    ParClockInclusion clL cl+  ParClockInR ::+    ParClockInclusion (ParallelClock m clL clR) cl ->+    ParClockInclusion clR cl   ParClockRefl :: ParClockInclusion cl cl --- | Generates a tag for the composite clock from a tag of a leaf clock,---   given a parallel clock inclusion.+{- | Generates a tag for the composite clock from a tag of a leaf clock,+   given a parallel clock inclusion.+-} parClockTagInclusion :: ParClockInclusion clS cl -> Tag clS -> Tag cl-parClockTagInclusion (ParClockInL parClockInL) tag = parClockTagInclusion parClockInL $ Left  tag+parClockTagInclusion (ParClockInL parClockInL) tag = parClockTagInclusion parClockInL $ Left tag parClockTagInclusion (ParClockInR parClockInR) tag = parClockTagInclusion parClockInR $ Right tag-parClockTagInclusion ParClockRefl              tag = tag+parClockTagInclusion ParClockRefl tag = tag
src/FRP/Rhine/Schedule/Concurrently.hs view
@@ -1,3 +1,7 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+ {- | Many clocks tick at nondeterministic times (such as event sources),@@ -6,10 +10,6 @@ Using concurrency, they can still be scheduled with all clocks in 'IO', by running the clocks in separate threads. -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.Schedule.Concurrently where  -- base@@ -29,53 +29,56 @@ import FRP.Rhine.Clock import FRP.Rhine.Schedule ---- | Runs two clocks in separate GHC threads---   and collects the results in the foreground thread.---   Caution: The data processing will still happen in the same thread---   (since data processing and scheduling are separated concerns).-concurrently-  :: ( Clock IO cl1, Clock IO cl2-     , Time cl1 ~ Time cl2-     )-  => Schedule IO cl1 cl2+{- | Runs two clocks in separate GHC threads+   and collects the results in the foreground thread.+   Caution: The data processing will still happen in the same thread+   (since data processing and scheduling are separated concerns).+-}+concurrently ::+  ( Clock IO cl1+  , Clock IO cl2+  , Time cl1 ~ Time cl2+  ) =>+  Schedule IO cl1 cl2 concurrently = Schedule $ \cl1 cl2 -> do   iMVar <- newEmptyMVar-  mvar  <- newEmptyMVar-  _ <- launchSubthread cl1 Left  iMVar mvar+  mvar <- newEmptyMVar+  _ <- launchSubthread cl1 Left iMVar mvar   _ <- launchSubthread cl2 Right iMVar mvar   initTime <- takeMVar iMVar -- The first clock to be initialised sets the first time stamp-  _        <- takeMVar iMVar -- Initialise the second clock+  _ <- takeMVar iMVar -- Initialise the second clock   return (constM $ takeMVar mvar, initTime)   where     launchSubthread cl leftright iMVar mvar = forkIO $ do       (runningClock, initTime) <- initClock cl       putMVar iMVar initTime       reactimate $ runningClock >>> second (arr leftright) >>> arrM (putMVar mvar)+ -- TODO These threads can't be killed from outside easily since we've lost their ids -- => make a MaybeT or ExceptT variant  -- TODO Test whether signal networks also share the writer and except effects correctly with these schedules --- | As 'concurrently', but in the @WriterT w IO@ monad.---   Both background threads share a joint variable with the foreground---   to which the writer effect writes.-concurrentlyWriter-  :: ( Monoid w-     , Clock (WriterT w IO) cl1-     , Clock (WriterT w IO) cl2-     , Time cl1 ~ Time cl2-     )-  => Schedule (WriterT w IO) cl1 cl2+{- | As 'concurrently', but in the @WriterT w IO@ monad.+   Both background threads share a joint variable with the foreground+   to which the writer effect writes.+-}+concurrentlyWriter ::+  ( Monoid w+  , Clock (WriterT w IO) cl1+  , Clock (WriterT w IO) cl2+  , Time cl1 ~ Time cl2+  ) =>+  Schedule (WriterT w IO) cl1 cl2 concurrentlyWriter = Schedule $ \cl1 cl2 -> do   iMVar <- lift newEmptyMVar-  mvar  <- lift newEmptyMVar-  _ <- launchSubthread cl1 Left  iMVar mvar+  mvar <- lift newEmptyMVar+  _ <- launchSubthread cl1 Left iMVar mvar   _ <- launchSubthread cl2 Right iMVar mvar   -- The first clock to be initialised sets the first time stamp   (initTime, w1) <- lift $ takeMVar iMVar-   -- Initialise the second clock-  (_       , w2) <- lift $ takeMVar iMVar+  -- Initialise the second clock+  (_, w2) <- lift $ takeMVar iMVar   tell w1   tell w2   return (constM (WriterT $ takeMVar mvar), initTime)@@ -83,35 +86,37 @@     launchSubthread cl leftright iMVar mvar = lift $ forkIO $ do       ((runningClock, initTime), w) <- runWriterT $ initClock cl       putMVar iMVar (initTime, w)-      reactimate $ runWriterS runningClock >>> proc (w', (time, tag_)) ->-        arrM (putMVar mvar) -< ((time, leftright tag_), w')+      reactimate $+        runWriterS runningClock >>> proc (w', (time, tag_)) ->+          arrM (putMVar mvar) -< ((time, leftright tag_), w') --- | Schedule in the @ExceptT e IO@ monad.---   Whenever one clock encounters an exception in 'ExceptT',---   this exception is thrown in the other clock's 'ExceptT' layer as well,---   and in the schedule's (i.e. in the main clock's) thread.-concurrentlyExcept-  :: ( Clock (ExceptT e IO) cl1-     , Clock (ExceptT e IO) cl2-     , Time cl1 ~ Time cl2-     )-  => Schedule (ExceptT e IO) cl1 cl2+{- | Schedule in the @ExceptT e IO@ monad.+   Whenever one clock encounters an exception in 'ExceptT',+   this exception is thrown in the other clock's 'ExceptT' layer as well,+   and in the schedule's (i.e. in the main clock's) thread.+-}+concurrentlyExcept ::+  ( Clock (ExceptT e IO) cl1+  , Clock (ExceptT e IO) cl2+  , Time cl1 ~ Time cl2+  ) =>+  Schedule (ExceptT e IO) cl1 cl2 concurrentlyExcept = Schedule $ \cl1 cl2 -> do   (iMVar, mvar, errorref) <- lift $ do     iMVar <- newEmptyMVar -- The initialisation time is transferred over this variable. It's written to twice.-    mvar  <- newEmptyMVar -- The ticks and exceptions are transferred over this variable. It receives two 'Left' values in total.+    mvar <- newEmptyMVar -- The ticks and exceptions are transferred over this variable. It receives two 'Left' values in total.     errorref <- newIORef Nothing -- Used to broadcast the exception to both clocks-    _ <- launchSubThread cl1 Left  iMVar mvar errorref+    _ <- launchSubThread cl1 Left iMVar mvar errorref     _ <- launchSubThread cl2 Right iMVar mvar errorref     return (iMVar, mvar, errorref)   catchAndDrain mvar $ do     initTime <- ExceptT $ takeMVar iMVar -- The first clock to be initialised sets the first time stamp-    _        <- ExceptT $ takeMVar iMVar -- Initialise the second clock+    _ <- ExceptT $ takeMVar iMVar -- Initialise the second clock     let runningSchedule = constM $ do           eTick <- lift $ takeMVar mvar           case eTick of             Right tick -> return tick-            Left e     -> do+            Left e -> do               lift $ writeIORef errorref $ Just e -- Broadcast the exception to both clocks               throwE e     return (runningSchedule, initTime)@@ -121,30 +126,34 @@       case initialised of         Right (runningClock, initTime) -> do           putMVar iMVar $ Right initTime-          Left e <- runExceptT $ reactimate $ runningClock >>> proc (td, tag2) -> do-            arrM (lift . putMVar mvar)               -< Right (td, leftright tag2)-            me <- constM (lift $ readIORef errorref) -< ()-            _  <- throwMaybe                         -< me-            returnA -< ()+          Left e <-+            runExceptT $+              reactimate $+                runningClock >>> proc (td, tag2) -> do+                  arrM (lift . putMVar mvar) -< Right (td, leftright tag2)+                  me <- constM (lift $ readIORef errorref) -< ()+                  _ <- throwMaybe -< me+                  returnA -< ()           putMVar mvar $ Left e -- Either throw own exception or acknowledge the exception from the other clock         Left e -> void $ putMVar iMVar $ Left e     catchAndDrain mvar initScheduleAction = catchE initScheduleAction $ \e -> do-      _ <- reactimate $ (constM $ ExceptT $ takeMVar mvar) >>> arr (const ()) -- Drain the mvar until the other clock acknowledges the exception+      _ <- reactimate $ constM (ExceptT $ takeMVar mvar) >>> arr (const ()) -- Drain the mvar until the other clock acknowledges the exception       throwE e  -- | As 'concurrentlyExcept', with a single possible exception value.-concurrentlyMaybe-  :: ( Clock (MaybeT IO) cl1-     , Clock (MaybeT IO) cl2-     , Time cl1 ~ Time cl2-     )-  => Schedule (MaybeT IO) cl1 cl2-concurrentlyMaybe = Schedule $ \cl1 cl2 -> initSchedule-  (hoistSchedule exceptTIOToMaybeTIO concurrentlyExcept)+concurrentlyMaybe ::+  ( Clock (MaybeT IO) cl1+  , Clock (MaybeT IO) cl2+  , Time cl1 ~ Time cl2+  ) =>+  Schedule (MaybeT IO) cl1 cl2+concurrentlyMaybe = Schedule $ \cl1 cl2 ->+  initSchedule+    (hoistSchedule exceptTIOToMaybeTIO concurrentlyExcept)     (HoistClock cl1 maybeTIOToExceptTIO)     (HoistClock cl2 maybeTIOToExceptTIO)-      where-        exceptTIOToMaybeTIO :: ExceptT () IO a -> MaybeT IO a-        exceptTIOToMaybeTIO = exceptToMaybeT-        maybeTIOToExceptTIO :: MaybeT IO a -> ExceptT () IO a-        maybeTIOToExceptTIO = maybeToExceptT ()+  where+    exceptTIOToMaybeTIO :: ExceptT () IO a -> MaybeT IO a+    exceptTIOToMaybeTIO = exceptToMaybeT+    maybeTIOToExceptTIO :: MaybeT IO a -> ExceptT () IO a+    maybeTIOToExceptTIO = maybeToExceptT ()
src/FRP/Rhine/Schedule/Trans.hs view
@@ -1,11 +1,11 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+ {- | Clocks implemented in the 'ScheduleT' monad transformer can always be scheduled (by construction). -}--{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.Schedule.Trans where  -- dunai@@ -16,59 +16,62 @@ import FRP.Rhine.Clock import FRP.Rhine.Schedule - -- * Universal schedule for the 'ScheduleT' monad transformer --- | Two clocks in the 'ScheduleT' monad transformer---   can always be canonically scheduled.---   Indeed, this is the purpose for which 'ScheduleT' was defined.-schedule-  :: ( Monad m-     , Clock (ScheduleT (Diff (Time cl1)) m) cl1-     , Clock (ScheduleT (Diff (Time cl1)) m) cl2-     , Time cl1 ~ Time cl2-     , Ord (Diff (Time cl1))-     , Num (Diff (Time cl1))-     )-  => Schedule (ScheduleT (Diff (Time cl1)) m) cl1 cl2+{- | Two clocks in the 'ScheduleT' monad transformer+   can always be canonically scheduled.+   Indeed, this is the purpose for which 'ScheduleT' was defined.+-}+schedule ::+  ( Monad m+  , Clock (ScheduleT (Diff (Time cl1)) m) cl1+  , Clock (ScheduleT (Diff (Time cl1)) m) cl2+  , Time cl1 ~ Time cl2+  , Ord (Diff (Time cl1))+  , Num (Diff (Time cl1))+  ) =>+  Schedule (ScheduleT (Diff (Time cl1)) m) cl1 cl2 schedule = Schedule {..}   where     initSchedule cl1 cl2 = do       (runningClock1, initTime) <- initClock cl1-      (runningClock2, _)        <- initClock cl2+      (runningClock2, _) <- initClock cl2       return         ( runningSchedule cl1 cl2 runningClock1 runningClock2         , initTime         )      -- Combines the two individual running clocks to one running clock.-    runningSchedule-      :: ( Monad m-         , Clock (ScheduleT (Diff (Time cl1)) m) cl1-         , Clock (ScheduleT (Diff (Time cl2)) m) cl2-         , Time cl1 ~ Time cl2-         , Ord (Diff (Time cl1))-         , Num (Diff (Time cl1))-         )-      => cl1 -> cl2-      -> MSF (ScheduleT (Diff (Time cl1)) m) () (Time cl1, Tag cl1)-      -> MSF (ScheduleT (Diff (Time cl1)) m) () (Time cl2, Tag cl2)-      -> MSF (ScheduleT (Diff (Time cl1)) m) () (Time cl1, Either (Tag cl1) (Tag cl2))+    runningSchedule ::+      ( Monad m+      , Clock (ScheduleT (Diff (Time cl1)) m) cl1+      , Clock (ScheduleT (Diff (Time cl2)) m) cl2+      , Time cl1 ~ Time cl2+      , Ord (Diff (Time cl1))+      , Num (Diff (Time cl1))+      ) =>+      cl1 ->+      cl2 ->+      MSF (ScheduleT (Diff (Time cl1)) m) () (Time cl1, Tag cl1) ->+      MSF (ScheduleT (Diff (Time cl1)) m) () (Time cl2, Tag cl2) ->+      MSF (ScheduleT (Diff (Time cl1)) m) () (Time cl1, Either (Tag cl1) (Tag cl2))     runningSchedule cl1 cl2 rc1 rc2 = MSF $ \_ -> do       -- Race both clocks against each other       raceResult <- race (unMSF rc1 ()) (unMSF rc2 ())       case raceResult of         -- The first clock ticks first...-        Left  (((time, tag1), rc1'), cont2) -> return-          -- so we can emit its time stamp...-          ( (time, Left tag1)-          -- and continue.-          , runningSchedule cl1 cl2 rc1' (MSF $ const cont2)-          )+        Left (((time, tag1), rc1'), cont2) ->+          return+            -- so we can emit its time stamp...+            ( (time, Left tag1)+            , -- and continue.+              runningSchedule cl1 cl2 rc1' (MSF $ const cont2)+            )         -- The second clock ticks first...-        Right (cont1, ((time, tag2), rc2')) -> return-          -- so we can emit its time stamp...-          ( (time, Right tag2)-          -- and continue.-          , runningSchedule cl1 cl2 (MSF $ const cont1) rc2'-          )+        Right (cont1, ((time, tag2), rc2')) ->+          return+            -- so we can emit its time stamp...+            ( (time, Right tag2)+            , -- and continue.+              runningSchedule cl1 cl2 (MSF $ const cont1) rc2'+            )
src/FRP/Rhine/Schedule/Util.hs view
@@ -1,20 +1,20 @@ -- | Utility to define certain deterministic schedules.- module FRP.Rhine.Schedule.Util where  -- dunai import Data.MonadicStreamFunction import Data.MonadicStreamFunction.Async --- | In a composite running clock,---   duplicate the tick of one subclock.+{- | In a composite running clock,+   duplicate the tick of one subclock.+-} duplicateSubtick :: Monad m => MSF m () (time, Either a b) -> MSF m () (time, Either a (Either a b)) duplicateSubtick runningClock = concatS $ runningClock >>> arr duplicateLeft   where-    duplicateLeft (time, Left a)  = [(time, Left a), (time, Right $ Left a)]+    duplicateLeft (time, Left a) = [(time, Left a), (time, Right $ Left a)]     duplicateLeft (time, Right b) = [(time, Right $ Right b)]  -- TODO Why is stuff like this not in base? Maybe send pull request... swapEither :: Either a b -> Either b a-swapEither (Left  a) = Right a-swapEither (Right b) = Left  b+swapEither (Left a) = Right a+swapEither (Right b) = Left b
src/FRP/Rhine/Type.hs view
@@ -1,25 +1,25 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-}+ {- | The type of a complete Rhine program: A signal network together with a matching clock value. -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE FlexibleContexts #-} module FRP.Rhine.Type where  -- dunai import Data.MonadicStreamFunction  -- rhine-import FRP.Rhine.Reactimation.ClockErasure import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy-import FRP.Rhine.SN+import FRP.Rhine.Reactimation.ClockErasure import FRP.Rhine.ResamplingBuffer (ResamplingBuffer)-import FRP.Rhine.Schedule (Out, In)+import FRP.Rhine.SN+import FRP.Rhine.Schedule (In, Out)  {- | A 'Rhine' consists of a 'SN' together with a clock of matching type 'cl'.@@ -34,14 +34,13 @@ using 'eraseClock'. -} data Rhine m cl a b = Rhine-  { sn    :: SN m cl a b+  { sn :: SN m cl a b   , clock :: cl   }  instance GetClockProxy cl => ToClockProxy (Rhine m cl a b) where   type Cl (Rhine m cl a b) = cl - {- | Start the clock and the signal network, effectively hiding the clock type from the outside.@@ -49,10 +48,10 @@ Since the caller will not know when the clock @'In' cl@ ticks, the input 'a' has to be given at all times, even those when it doesn't tick. -}-eraseClock-  :: (Monad m, Clock m cl, GetClockProxy cl)-  => Rhine  m cl a        b-  -> m (MSF m    a (Maybe b))+eraseClock ::+  (Monad m, Clock m cl, GetClockProxy cl) =>+  Rhine m cl a b ->+  m (MSF m a (Maybe b)) eraseClock Rhine {..} = do   (runningClock, initTime) <- initClock clock   -- Run the main loop@@ -66,15 +65,17 @@ Since output and input will generally tick at different clocks, the data needs to be resampled. -}-feedbackRhine-  :: ( Clock m (In cl),  Clock m (Out cl)-     , Time (In cl) ~ Time cl-     , Time (Out cl) ~ Time cl-     )-  => ResamplingBuffer m (Out cl) (In cl) d c-  -> Rhine            m cl (a, c) (b, d)-  -> Rhine            m cl  a      b-feedbackRhine buf Rhine { .. } = Rhine-  { sn = Feedback buf sn-  , clock-  }+feedbackRhine ::+  ( Clock m (In cl)+  , Clock m (Out cl)+  , Time (In cl) ~ Time cl+  , Time (Out cl) ~ Time cl+  ) =>+  ResamplingBuffer m (Out cl) (In cl) d c ->+  Rhine m cl (a, c) (b, d) ->+  Rhine m cl a b+feedbackRhine buf Rhine {..} =+  Rhine+    { sn = Feedback buf sn+    , clock+    }