rhine 0.2.0.0 → 0.3.0.0
raw patch · 4 files changed
+84/−109 lines, 4 filesdep ~dunaiPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: dunai
API changes (from Hackage documentation)
+ FRP.Rhine.SyncSF: type BehaviorF m td a b = BehaviourF m td a b
+ FRP.Rhine.SyncSF.Except: keepFirst :: Monad m => SyncSF m cl a a
+ FRP.Rhine.SyncSF.Except: scaledTimer :: (Monad m, TimeDomain td, Fractional (Diff td), Ord (Diff td)) => Diff td -> BehaviorF (ExceptT () m) td a (Diff td)
+ FRP.Rhine.SyncSF.Except: timer :: (Monad m, TimeDomain td, Ord (Diff td)) => Diff td -> BehaviorF (ExceptT () m) td a (Diff td)
+ FRP.Rhine.SyncSF.Except: type BehaviorFExcept m td a b e = BehaviourFExcept m td a b e
- FRP.Rhine.SyncSF: average :: (Monad m, VectorSpace v, Floating (Groundfield v), Groundfield v ~ Diff (TimeDomainOf cl)) => Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: average :: (Monad m, VectorSpace v, Floating (Groundfield v), Groundfield v ~ Diff td) => Diff td -> BehaviourF m td v v
- FRP.Rhine.SyncSF: averageFrom :: (Monad m, VectorSpace v, Floating (Groundfield v), Groundfield v ~ Diff (TimeDomainOf cl)) => v -> Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: averageFrom :: (Monad m, VectorSpace v, Floating (Groundfield v), Groundfield v ~ Diff td) => v -> Diff td -> BehaviorF m td v v
- FRP.Rhine.SyncSF: averageLin :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: averageLin :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => Diff td -> BehaviourF m td v v
- FRP.Rhine.SyncSF: averageLinFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: averageLinFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => v -> Diff td -> BehaviourF m td v v
- FRP.Rhine.SyncSF: derivative :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => SyncSF m cl v v
+ FRP.Rhine.SyncSF: derivative :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => BehaviorF m td v v
- FRP.Rhine.SyncSF: derivativeFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: derivativeFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => v -> BehaviorF m td v v
- FRP.Rhine.SyncSF: integral :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => SyncSF m cl v v
+ FRP.Rhine.SyncSF: integral :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => BehaviorF m td v v
- FRP.Rhine.SyncSF: integralFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: integralFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => v -> BehaviorF m td v v
- FRP.Rhine.SyncSF: type SyncSF m cl a b = MSF (ReaderT (TimeInfo cl) m) a b
+ FRP.Rhine.SyncSF: type BehaviourF m td a b = forall cl. td ~ TimeDomainOf cl => SyncSF m cl a b
- FRP.Rhine.SyncSF: weightedAverageFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> SyncSF m cl (v, Groundfield v) v
+ FRP.Rhine.SyncSF: weightedAverageFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff td) => v -> BehaviorF m td (v, Groundfield v) v
- FRP.Rhine.SyncSF.Except: type SyncExcept m cl a b e = MSFExcept (ReaderT (TimeInfo cl) m) a b e
+ FRP.Rhine.SyncSF.Except: type BehaviourFExcept m td a b e = forall cl. td ~ TimeDomainOf cl => SyncExcept m cl a b e
Files
- README.md +2/−80
- rhine.cabal +3/−3
- src/FRP/Rhine/SyncSF.hs +33/−25
- src/FRP/Rhine/SyncSF/Except.hs +46/−1
README.md view
@@ -1,82 +1,4 @@-# README+* README -------- -[](https://travis-ci.org/turion/rhine)--Rhine is a library for synchronous and asynchronous Functional Reactive Programming (FRP).-It separates the aspects of clocking, scheduling and resampling-from each other, and ensures clock-safety on the type level.--Complex reactive programs often process data at different rates.-For example, games, GUIs and media applications-may output audio and video signals, or receive-user input at unpredictable times.-Coordinating these different rates is a hard problem in general.-If not enough care is taken, buffer underruns and overflows, space and time leaks,-accidental synchronisation of independent sub-systems,-and concurrency issues such as dead-locks may all occur.--Rhine tackles these problems by annotating-the signal processing components with clocks,-which hold the information when data will be-input, processed and output.-Different components of the signal network-will become active at different times, or work-at different rates. If components running under different clocks need to communicate, it-has to be decided when each component be--comes active ("scheduling"), and how data is-transferred between the different rates ("resampling").-Rhine separates all these aspects from each-other, and from the individual signal processing of each subsystem.-It offers a flexible API to all of them and implements several-reusable standard solutions. In the places-where these aspects need to intertwine, typing-constraints on clocks come into effect, enforcing clock safety.--A typical example, which can be run as `cabal run Demonstration`,-would be:--```- -- | Create a simple message containing the time stamp since program start,- -- for each tick of the clock.- -- Since 'createMessage' works for arbitrary clocks (and doesn't need further input data),- -- it is a 'Behaviour'.- -- @td@ is the 'TimeDomain' of any clock used to sample,- -- and it needs to be constrained in order for time differences- -- to have a 'Show' instance.- createMessage- :: (Monad m, Show (Diff td))- => String- -> Behaviour m td String- createMessage str- = timeInfoOf sinceStart >-> arr show- >-> arr (("Clock " ++ str ++ " has ticked at: ") ++)-- -- | Output a message /every second/ (= every 1000 milliseconds).- -- Let us assume we want to assure that 'printEverySecond'- -- is only called every second,- -- then we constrain its type signature with the clock @Millisecond 1000@.- printEverySecond :: Show a => SyncSF IO (Millisecond 1000) a ()- printEverySecond = arrMSync print-- -- | Specialise 'createMessage' to a specific clock.- ms500 :: SyncSF IO (Millisecond 500) () String- ms500 = createMessage "500 MS"--- ms1200 :: SyncSF IO (Millisecond 1200) () String- ms1200 = createMessage "1200 MS"-- -- | Create messages every 500 ms and every 1200 ms,- -- collecting all of them in a list,- -- which is output every second.- main :: IO ()- main = flow $- ms500 @@ waitClock **@ concurrently @** ms1200 @@ waitClock- >-- collect -@- concurrently -->- printEverySecond @@ waitClock-- -- | Uncomment the following for a type error (the clocks don't match):-- -- typeError = ms500 >>> printEverySecond-```+This is the main library.
rhine.cabal view
@@ -1,6 +1,6 @@ name: rhine -version: 0.2.0.0+version: 0.3.0.0 synopsis: Functional Reactive Programming with type-level clocks @@ -44,7 +44,7 @@ source-repository this type: git location: git@github.com:turion/rhine.git- tag: v0.2.0.0+ tag: v0.3.0.0 library@@ -84,7 +84,7 @@ -- Other library packages from which modules are imported. build-depends: base >= 4.7 && < 5- , dunai == 0.2.0.*+ , dunai == 0.3.0.* , transformers >= 0.4 && < 0.6 , time >= 1.6 && < 1.7 , free >= 4.12 && < 4.13
src/FRP/Rhine/SyncSF.hs view
@@ -30,22 +30,30 @@ -- | A (synchronous) monadic stream function -- with the additional side effect of being time-aware,--- that is, reading the current 'TimeInfo' of the clock 'cl'.+-- that is, reading the current 'TimeInfo' of the clock @cl@. type SyncSF m cl a b = MSF (ReaderT (TimeInfo cl) m) a b --- | A synchronous signal is a |SyncSF| with no input required.+-- | A synchronous signal is a 'SyncSF' with no input required. -- It produces its output on its own. type SyncSignal m cl a = SyncSF m cl () a -- | A (side-effectful) behaviour is a time-aware stream -- that doesn't depend on a particular clock.--- 'td' denotes the |TimeDomain|.+-- @td@ denotes the 'TimeDomain'. type Behaviour m td a = forall cl. td ~ TimeDomainOf cl => SyncSignal m cl a -- | Compatibility to U.S. american spelling. type Behavior m td a = Behaviour m td a +-- | A (side-effectful) behaviour function is a time-aware synchronous stream+-- function that doesn't depend on a particular clock.+-- @td denotes the 'TimeDomain'.+type BehaviourF m td a b = forall cl. td ~ TimeDomainOf cl => SyncSF m cl a b +-- | Compatibility to U.S. american spelling.+type BehaviorF m td a b = BehaviourF m td a b++ -- * Utilities to create 'SyncSF's from simpler data -- TODO Test in which situations it makes sense not to change cl@@ -131,8 +139,8 @@ -- of the input, with initial offset @v0@. integralFrom :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))- => v -> SyncSF m cl v v+ , Groundfield v ~ Diff td)+ => v -> BehaviorF m td v v integralFrom v0 = proc v -> do _sinceTick <- timeInfoOf sinceTick -< () sumFrom v0 -< _sinceTick *^ v@@ -140,8 +148,8 @@ -- | Euler integration, with zero initial offset. integral :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))- => SyncSF m cl v v+ , Groundfield v ~ Diff td)+ => BehaviorF m td v v integral = integralFrom zeroVector @@ -150,8 +158,8 @@ -- The input is initialised with @v0@. derivativeFrom :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))- => v -> SyncSF m cl v v+ , Groundfield v ~ Diff td)+ => v -> BehaviorF m td v v derivativeFrom v0 = proc v -> do vLast <- delay v0 -< v TimeInfo {..} <- timeInfo -< ()@@ -160,8 +168,8 @@ -- | Numerical derivative with input initialised to zero. derivative :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))- => SyncSF m cl v v+ , Groundfield v ~ Diff td)+ => BehaviorF m td v v derivative = derivativeFrom zeroVector -- | A weighted moving average signal function.@@ -173,9 +181,9 @@ -- whereas a weight of 0 outputs the current value. weightedAverageFrom :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))+ , Groundfield v ~ Diff td) => v -- ^ The initial position- -> SyncSF m cl (v, Groundfield v) v+ -> BehaviorF m td (v, Groundfield v) v weightedAverageFrom v0 = feedback v0 $ proc ((v, weight), vAvg) -> do let vAvg' = weight *^ vAvg ^+^ (1 - weight) *^ v@@ -187,10 +195,10 @@ averageFrom :: ( Monad m, VectorSpace v , Floating (Groundfield v)- , Groundfield v ~ Diff (TimeDomainOf cl))+ , Groundfield v ~ Diff td) => v -- ^ The initial position- -> Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged- -> SyncSF m cl v v+ -> Diff td -- ^ The time scale on which the signal is averaged+ -> BehaviorF m td v v averageFrom v0 t = proc v -> do TimeInfo {..} <- timeInfo -< () let@@ -202,9 +210,9 @@ average :: ( Monad m, VectorSpace v , Floating (Groundfield v)- , Groundfield v ~ Diff (TimeDomainOf cl))- => Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged- -> SyncSF m cl v v+ , Groundfield v ~ Diff td)+ => Diff td -- ^ The time scale on which the signal is averaged+ -> BehaviourF m td v v average = averageFrom zeroVector -- | A linearised version of 'averageFrom'.@@ -213,10 +221,10 @@ -- than the average time difference between two ticks. averageLinFrom :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))+ , Groundfield v ~ Diff td) => v -- ^ The initial position- -> Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged- -> SyncSF m cl v v+ -> Diff td -- ^ The time scale on which the signal is averaged+ -> BehaviourF m td v v averageLinFrom v0 t = proc v -> do TimeInfo {..} <- timeInfo -< () let@@ -226,7 +234,7 @@ -- | Linearised version of 'average'. averageLin :: ( Monad m, VectorSpace v- , Groundfield v ~ Diff (TimeDomainOf cl))- => Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged- -> SyncSF m cl v v+ , Groundfield v ~ Diff td)+ => Diff td -- ^ The time scale on which the signal is averaged+ -> BehaviourF m td v v averageLin = averageLinFrom zeroVector
src/FRP/Rhine/SyncSF/Except.hs view
@@ -1,4 +1,8 @@-{-# LANGUAGE Arrows #-}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+ module FRP.Rhine.SyncSF.Except ( module FRP.Rhine.SyncSF.Except , module X@@ -22,6 +26,13 @@ type SyncExcept m cl a b e = MSFExcept (ReaderT (TimeInfo cl) m) a b e +type BehaviourFExcept m td a b e+ = forall cl. td ~ TimeDomainOf cl => SyncExcept m cl a b e++-- | Compatibility to U.S. american spelling.+type BehaviorFExcept m td a b e = BehaviourFExcept m td a b e++ -- | Commute the effects of the |ReaderT| and the |ExceptT| monad. commuteReaderExcept :: ReaderT r (ExceptT e m) a -> ExceptT e (ReaderT r m) a commuteReaderExcept a = ExceptT $ ReaderT $ \r -> runExceptT $ runReaderT a r@@ -57,3 +68,37 @@ -- and then throws an exception. step :: Monad m => (a -> m (b, e)) -> SyncExcept m cl a b e step f = MSFE.step $ lift . f++-- | Remembers and indefinitely outputs the first input value.+keepFirst :: Monad m => SyncSF m cl a a+keepFirst = safely $ do+ a <- try throwS+ safe $ arr $ const a+++-- | Throws an exception after the specified time difference,+-- outputting the remaining time difference.+timer+ :: ( Monad m+ , TimeDomain td+ , Ord (Diff td)+ )+ => Diff td+ -> BehaviorF (ExceptT () m) td a (Diff td)+timer diff = proc _ -> do+ time <- timeInfoOf absolute -< ()+ startTime <- keepFirst -< time+ let remainingTime = time `diffTime` startTime+ _ <- throwOn () -< remainingTime > diff+ returnA -< remainingTime++-- | 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 diff = timer diff >>> arr (/ diff)