rhine 0.6.0 → 0.7.0
raw patch · 22 files changed
+339/−281 lines, 22 filesdep ~MonadRandomdep ~containersdep ~deepseq
Dependency ranges changed: MonadRandom, containers, deepseq, free, random, transformers, vector-sized
Files
- ChangeLog.md +8/−0
- rhine.cabal +13/−11
- src/FRP/Rhine.hs +2/−0
- src/FRP/Rhine/Clock.hs +0/−17
- src/FRP/Rhine/Clock/FixedStep.hs +3/−0
- src/FRP/Rhine/Clock/Periodic.hs +3/−0
- src/FRP/Rhine/Clock/Proxy.hs +88/−0
- src/FRP/Rhine/Clock/Realtime/Audio.hs +5/−1
- src/FRP/Rhine/Clock/Realtime/Busy.hs +3/−0
- src/FRP/Rhine/Clock/Realtime/Event.hs +3/−0
- src/FRP/Rhine/Clock/Realtime/Millisecond.hs +2/−0
- src/FRP/Rhine/Clock/Realtime/Stdin.hs +3/−0
- src/FRP/Rhine/Clock/Select.hs +2/−0
- src/FRP/Rhine/Clock/Util.hs +24/−0
- src/FRP/Rhine/Reactimation.hs +11/−18
- src/FRP/Rhine/Reactimation/ClockErasure.hs +111/−0
- src/FRP/Rhine/Reactimation/Combinators.hs +8/−2
- src/FRP/Rhine/Reactimation/Tick.hs +0/−231
- src/FRP/Rhine/ResamplingBuffer.hs +0/−1
- src/FRP/Rhine/SN.hs +11/−0
- src/FRP/Rhine/SN/Combinators.hs +6/−0
- src/FRP/Rhine/Type.hs +33/−0
ChangeLog.md view
@@ -4,6 +4,14 @@ Since `rhine` reexports modules from `dunai`, every major version in `dunai` triggers a major version in `rhine`. +## 0.7.0++* Replaced old reactimation mechanism by clock erasure+* Dropped GHC support for < 8.4+* Reworked `gloss` backends.+ There are now two pure backends and an `IO` backend.+* Relaxed all upper version bounds+ ## 0.6.0 * Synced with `dunai` version numbers
rhine.cabal view
@@ -1,6 +1,6 @@ name: rhine -version: 0.6.0+version: 0.7.0 synopsis: Functional Reactive Programming with type-level clocks @@ -46,7 +46,7 @@ source-repository this type: git location: git@github.com:turion/rhine.git- tag: v0.6.0+ tag: v0.7.0 library@@ -54,14 +54,16 @@ Control.Monad.Schedule FRP.Rhine FRP.Rhine.Clock+ FRP.Rhine.Clock.FixedStep FRP.Rhine.Clock.Periodic+ FRP.Rhine.Clock.Proxy FRP.Rhine.Clock.Realtime.Audio FRP.Rhine.Clock.Realtime.Busy FRP.Rhine.Clock.Realtime.Event FRP.Rhine.Clock.Realtime.Millisecond FRP.Rhine.Clock.Realtime.Stdin FRP.Rhine.Clock.Select- FRP.Rhine.Clock.FixedStep+ FRP.Rhine.Clock.Util FRP.Rhine.ClSF FRP.Rhine.ClSF.Core FRP.Rhine.ClSF.Except@@ -70,7 +72,7 @@ FRP.Rhine.ClSF.Upsample FRP.Rhine.ClSF.Util FRP.Rhine.Reactimation- FRP.Rhine.Reactimation.Tick+ FRP.Rhine.Reactimation.ClockErasure FRP.Rhine.Reactimation.Combinators FRP.Rhine.ResamplingBuffer FRP.Rhine.ResamplingBuffer.Collect@@ -100,14 +102,14 @@ -- Other library packages from which modules are imported. build-depends: base >= 4.9 && < 5 , dunai >= 0.6- , transformers == 0.5.*+ , transformers >= 0.5 , time >= 1.8- , free == 5.1.*- , containers == 0.6.*- , vector-sized == 1.4.*- , deepseq == 1.4.*- , random == 1.1.*- , MonadRandom == 0.5.*+ , free >= 5.1+ , containers >= 0.5+ , vector-sized >= 1.4+ , deepseq >= 1.4+ , random >= 1.1+ , MonadRandom >= 0.5 , simple-affine-space -- Directories containing source files.
src/FRP/Rhine.hs view
@@ -20,6 +20,8 @@ -- 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.Reactimation.Combinators as X
src/FRP/Rhine/Clock.hs view
@@ -70,7 +70,6 @@ :: 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 - -- * Auxiliary definitions and utilities -- | An annotated, rich time stamp.@@ -91,22 +90,6 @@ => (Tag cl1 -> Tag cl2) -> TimeInfo cl1 -> TimeInfo cl2 retag f TimeInfo {..} = TimeInfo { tag = f tag, .. }----- | Given a clock value and an initial time,--- generate a stream of time stamps.-genTimeInfo- :: (Monad m, Clock m cl)- => 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- , ..- }- -- * Certain universal building blocks to produce new clocks from given ones
src/FRP/Rhine/Clock/FixedStep.hs view
@@ -26,6 +26,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.ResamplingBuffer import FRP.Rhine.ResamplingBuffer.Collect import FRP.Rhine.ResamplingBuffer.Util@@ -50,6 +51,8 @@ &&& arr (const ()) , 0 )++instance GetClockProxy (FixedStep n) -- | A singleton clock that counts the ticks. type Count = FixedStep 1
src/FRP/Rhine/Clock/Periodic.hs view
@@ -27,6 +27,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import Control.Monad.Schedule -- * The 'Periodic' clock@@ -48,6 +49,8 @@ ( cycleS (theList cl) >>> withSideEffect wait >>> (accumulateWith (+) 0) &&& arr (const ()) , 0 )++instance GetClockProxy (Periodic v) -- * Type-level trickery to extract the type value from the singleton
+ src/FRP/Rhine/Clock/Proxy.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE TypeFamilies #-}+module FRP.Rhine.Clock.Proxy where++-- base+import Data.Kind (Type)++-- rhine+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.+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)++inProxy :: ClockProxy cl -> ClockProxy (In cl)+inProxy LeafProxy = LeafProxy+inProxy (SequentialProxy p1 p2) = inProxy p1+inProxy (ParallelProxy pL pR) = ParallelProxy (inProxy pL) (inProxy pR)++outProxy :: ClockProxy cl -> ClockProxy (Out cl)+outProxy LeafProxy = LeafProxy+outProxy (SequentialProxy p1 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.+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 (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.+outTag :: ClockProxy cl -> Tag cl -> Maybe (Tag (Out cl))+outTag (SequentialProxy _ _ ) (Left _) = Nothing+outTag (SequentialProxy _ p2) (Right tag2) = outTag p2 tag2+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+ getClockProxy = LeafProxy++instance (GetClockProxy cl1, GetClockProxy cl2) => GetClockProxy (SequentialClock m cl1 cl2) where+ getClockProxy = SequentialProxy getClockProxy getClockProxy++instance (GetClockProxy cl1, GetClockProxy cl2) => GetClockProxy (ParallelClock m cl1 cl2) where+ getClockProxy = ParallelProxy getClockProxy getClockProxy++instance GetClockProxy cl => GetClockProxy (HoistClock m1 m2 cl)+instance GetClockProxy cl => GetClockProxy (RescaledClock cl time)+instance GetClockProxy cl => GetClockProxy (RescaledClockM m cl time)+instance GetClockProxy cl => GetClockProxy (RescaledClockS m cl time tag)++-- | Extract a clock proxy from a type.+class ToClockProxy a where+ type Cl a :: Type++ toClockProxy :: a -> ClockProxy (Cl a)++ default toClockProxy+ :: GetClockProxy (Cl a)+ => a -> ClockProxy (Cl a)+ toClockProxy _ = getClockProxy
src/FRP/Rhine/Clock/Realtime/Audio.hs view
@@ -36,6 +36,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy -- | Rates at which audio signals are typically sampled. data AudioRate@@ -122,6 +123,8 @@ , initialTime ) +instance GetClockProxy (AudioClock rate bufferSize)+ {- | A side-effect free clock for audio synthesis and analysis. The sample rate is given by 'rate' (of type 'AudioRate').@@ -149,6 +152,7 @@ , 0 ) +instance GetClockProxy (PureAudioClock rate) -- | A rescaled version of 'PureAudioClock' with 'TimeDomain' 'Float'. type PureAudioClockF (rate :: AudioRate) = RescaledClock (PureAudioClock rate) Float@@ -160,4 +164,4 @@ pureAudioClockF = RescaledClock { unscaledClock = PureAudioClock , rescale = double2Float-}+ }
src/FRP/Rhine/Clock/Realtime/Busy.hs view
@@ -9,6 +9,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy {- | A clock that ticks without waiting.@@ -28,3 +29,5 @@ &&& arr (const ()) , initialTime )++instance GetClockProxy Busy
src/FRP/Rhine/Clock/Realtime/Event.hs view
@@ -43,6 +43,7 @@ import Control.Monad.Trans.Reader -- rhine+import FRP.Rhine.Clock.Proxy import FRP.Rhine.ClSF import FRP.Rhine.Schedule import FRP.Rhine.Schedule.Concurrently@@ -160,6 +161,8 @@ 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'
src/FRP/Rhine/Clock/Realtime/Millisecond.hs view
@@ -21,6 +21,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.Clock.FixedStep import FRP.Rhine.Schedule import FRP.Rhine.ResamplingBuffer@@ -46,6 +47,7 @@ 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.
src/FRP/Rhine/Clock/Realtime/Stdin.hs view
@@ -18,6 +18,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import Data.Semigroup {- |@@ -39,6 +40,8 @@ return (time, line) , initialTime )++instance GetClockProxy StdinClock instance Semigroup StdinClock where _ <> _ = StdinClock
src/FRP/Rhine/Clock/Select.hs view
@@ -16,6 +16,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.Schedule -- dunai@@ -50,6 +51,7 @@ 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).
+ src/FRP/Rhine/Clock/Util.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE RecordWildCards #-}+module FRP.Rhine.Clock.Util where++-- rhine+import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy+import FRP.Rhine.TimeDomain++-- * 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)+genTimeInfo _ initialTime = proc (absolute, tag) -> do+ lastTime <- iPre initialTime -< absolute+ returnA -< TimeInfo+ { sinceLast = absolute `diffTime` lastTime+ , sinceInit = absolute `diffTime` initialTime+ , ..+ }
src/FRP/Rhine/Reactimation.hs view
@@ -3,18 +3,23 @@ as main loops. -} +{-# LANGUAGE Arrows #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RecordWildCards #-} module FRP.Rhine.Reactimation where +-- base+import Control.Monad ((>=>))+import Data.Functor (void) -- dunai import Data.MonadicStreamFunction.InternalCore -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.ClSF.Core-import FRP.Rhine.Reactimation.Tick+import FRP.Rhine.Reactimation.ClockErasure import FRP.Rhine.Reactimation.Combinators import FRP.Rhine.Schedule import FRP.Rhine.Type@@ -53,32 +58,20 @@ -- 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 Rhine {..} = do- (runningClock, initTime) <- initClock clock- -- Run the main loop- flow' runningClock $ createTickable- (trivialResamplingBuffer clock)- sn- (trivialResamplingBuffer clock)- initTime- where- flow' runningClock tickable = do- -- Fetch the next time stamp from the stream, wait if necessary- ((now, tag), runningClock') <- unMSF runningClock ()- -- Process the part of the signal network that is scheduled to run- tickable' <- tick tickable now tag- -- Loop- flow' runningClock' tickable'-+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 ()
+ src/FRP/Rhine/Reactimation/ClockErasure.hs view
@@ -0,0 +1,111 @@+{- |+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 RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+module FRP.Rhine.Reactimation.ClockErasure where++-- base+import Control.Monad (join)+import Data.Maybe (fromJust, fromMaybe)++-- dunai+import Control.Monad.Trans.MSF.Reader+import Data.MonadicStreamFunction+import Data.MonadicStreamFunction.InternalCore++-- rhine+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.Schedule+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+eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do+ timeInfo <- genTimeInfo proxy initialTime -< (time, tag)+ 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)++-- 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++-- A sequentially composed signal network may either be triggered in its first component,+-- or its second component. In either case,+-- the resampling buffer (which connects the two components) may be triggered,+-- but only if the outgoing clock of the first component ticks,+-- or the incoming clock of the second component ticks.+eraseClockSN initialTime (Sequential sn1 resBuf sn2) =+ 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)++eraseClockSN initialTime (Parallel snL snR) = proc (time, tag, maybeA) -> do+ case tag of+ Left tagL -> eraseClockSN initialTime snL -< (time, tagL, maybeA)+ Right tagR -> eraseClockSN initialTime snR -< (time, tagR, maybeA)++-- | 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')+ Right (time2, tag2) -> do+ timeInfo2 <- genTimeInfo proxy2 initialTime -< (time2, tag2)+ (b, resBuf') <- arrM (uncurry get) -< (resBuf, timeInfo2)+ returnA -< (Just b, resBuf')
src/FRP/Rhine/Reactimation/Combinators.hs view
@@ -21,6 +21,7 @@ -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.ClSF.Core import FRP.Rhine.ResamplingBuffer import FRP.Rhine.Schedule@@ -98,8 +99,9 @@ , Time cl1 ~ Time cl2 , Time (Out cl1) ~ Time cl1 , Time (In cl2) ~ Time cl2- , Clock m (Out cl1)- , Clock m (In 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@@ -139,6 +141,8 @@ 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@@ -176,6 +180,8 @@ 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
− src/FRP/Rhine/Reactimation/Tick.hs
@@ -1,231 +0,0 @@-{- |-This module contains internals needed for the reactimation of signal functions.-None of it should be relevant for a typical user of this library.--}--{-# LANGUAGE GADTs #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RecordWildCards #-}-module FRP.Rhine.Reactimation.Tick where---- transformers-import Control.Monad.Trans.Reader---- dunai-import Data.MonadicStreamFunction-import Data.MonadicStreamFunction.InternalCore---- rhine-import FRP.Rhine.Clock-import FRP.Rhine.ResamplingBuffer-import FRP.Rhine.Schedule-import FRP.Rhine.SN---{- | A signal network ('SN') enclosed by matching 'ResamplingBuffer's and further auxiliary data,-such that it can be stepped with each arriving tick from a clock 'cl'.-They play a similar role like 'ReactHandle's in dunai.--The type parameters:--* 'm': The monad in which the 'SN' and the 'ResamplingBuffer's produce side effects-* 'cla': The (irrelevant) input clock of the left 'ResamplingBuffer'-* 'clb': The clock at which the left 'ResamplingBuffer' produces output-* 'cl': The clock at which the 'SN' ticks-* 'clc': The clock at which the right 'ResamplingBuffer' accepts input-* 'cld': The (irrelevant) output clock of the right 'ResamplingBuffer'-* 'a': The (irrelevant) input type of the left 'ResamplingBuffer'-* 'b': The input type of the 'SN'-* 'c': The output type of the 'SN'-* 'd': The (irrelevant) output type of the right 'ResamplingBuffer'--}-data Tickable m cla clb cl clc cld a b c d = Tickable- { -- | The left buffer from which the input is taken.- buffer1 :: ResamplingBuffer m cla clb a b- -- | The signal network that will process the data.- , ticksn :: SN m cl b c- -- | The right buffer in which the output is stored.- , buffer2 :: ResamplingBuffer m clc cld c d- -- | The leftmost clock of the signal network, 'cl',- -- may be a parallel subclock of the buffer clock.- -- 'parClockIn' specifies in which position 'In cl'- -- is a parallel subclock of 'clb'.- , parClockIn :: ParClockInclusion (In cl) clb- -- | The same on the output side.- , parClockOut :: ParClockInclusion (Out cl) clc- -- | The last times when the different parts of the signal tree have ticked.- , lastTime :: LastTime cl- -- | The time when the whole clock was initialised.- , initTime :: Time cl- }----- | Initialise the tree of last tick times.-initLastTime :: SN m cl a b -> Time cl -> LastTime cl-initLastTime (Synchronous _) initTime = LeafLastTime initTime-initLastTime (Sequential sn1 _ sn2) initTime =- SequentialLastTime- (initLastTime sn1 initTime)- (initLastTime sn2 initTime)-initLastTime (Parallel sn1 sn2) initTime =- ParallelLastTime- (initLastTime sn1 initTime)- (initLastTime sn2 initTime)---- | Initialise a 'Tickable' from a signal network,--- two matching enclosing resampling buffers and an initial time.-createTickable- :: ResamplingBuffer m cla (In cl) a b- -> SN m cl b c- -> ResamplingBuffer m (Out cl) cld c d- -> Time cl- -> Tickable m cla (In cl) cl (Out cl) cld a b c d-createTickable buffer1 ticksn buffer2 initTime = Tickable- { parClockIn = ParClockRefl- , parClockOut = ParClockRefl- , lastTime = initLastTime ticksn initTime- , ..- }--{- | In this function, one tick, or step of an asynchronous signal network happens.-The 'TimeInfo' holds the information which part of the signal tree will tick.-This information is encoded in the 'Tag' of the 'TimeInfo',-which is of type 'Either tag1 tag2' in case of a 'SequentialClock' or a 'ParallelClock',-encoding either a tick for the left clock or the right clock.--}-tick :: ( Monad m, Clock m cl- , Time cla ~ Time cl- , Time clb ~ Time cl- , Time clc ~ Time cl- , Time cld ~ Time cl- , Time (In cl) ~ Time cl- , Time (Out cl) ~ Time cl- )- => Tickable m cla clb cl clc cld a b c d- -> Time cl -- ^ Timestamp of the present tick- -> Tag cl -- ^ 'Tag' of the overall clock; contains the information which subsystem will become active- -> m (Tickable m cla clb cl clc cld a b c d)--- Only if we have reached a leaf of the tree, data is actually processed.-tick Tickable- { ticksn = Synchronous clsf- , lastTime = LeafLastTime lastTime- , .. } now tag = do- let- ti = TimeInfo- { sinceLast = diffTime now lastTime- , sinceInit = diffTime now initTime- , absolute = now- , tag = tag- }- -- Get an input value from the left buffer- (b, buffer1') <- get buffer1 $ retag (parClockTagInclusion parClockIn ) ti- -- Run it through the signal function- (c, clsf') <- unMSF clsf b `runReaderT` ti- -- Put the output into the right buffer- buffer2' <- put buffer2 (retag (parClockTagInclusion parClockOut) ti) c- return Tickable- { buffer1 = buffer1'- , ticksn = Synchronous clsf'- , buffer2 = buffer2'- , lastTime = LeafLastTime now- , .. }--- The left part of a sequential composition is stepped.-tick tickable@Tickable- { ticksn = Sequential sn1 bufferMiddle sn2- , lastTime = SequentialLastTime lastTimeL lastTimeR- , initTime- , parClockIn- } now (Left tag) = do- leftTickable <- tick Tickable- { buffer1 = buffer1 tickable- , ticksn = sn1- , buffer2 = bufferMiddle- , parClockIn = parClockIn- , parClockOut = ParClockRefl- , lastTime = lastTimeL- , initTime = initTime- } now tag- return $ tickable- { buffer1 = buffer1 leftTickable- , ticksn = Sequential (ticksn leftTickable) (buffer2 leftTickable) sn2- , lastTime = SequentialLastTime (lastTime leftTickable) lastTimeR- }--- The right part of a sequential composition is stepped.-tick tickable@Tickable- { ticksn = Sequential sn1 bufferMiddle sn2- , lastTime = SequentialLastTime lastTimeL lastTimeR- , initTime- , parClockOut- } now (Right tag) = do- rightTickable <- tick Tickable- { buffer1 = bufferMiddle- , ticksn = sn2- , buffer2 = buffer2 tickable- , parClockIn = ParClockRefl- , parClockOut = parClockOut- , lastTime = lastTimeR- , initTime = initTime- } now tag- return $ tickable- { buffer2 = buffer2 rightTickable- , ticksn = Sequential sn1 (buffer1 rightTickable) (ticksn rightTickable)- , lastTime = SequentialLastTime lastTimeL (lastTime rightTickable)- }--- A parallel composition is stepped.-tick tickable@Tickable- { ticksn = Parallel snA snB- , lastTime = ParallelLastTime lastTimeA lastTimeB- , initTime- , parClockIn- , parClockOut- } now tag = case tag of- Left tagL -> do- leftTickable <- tick Tickable- { buffer1 = buffer1 tickable- , ticksn = snA- , buffer2 = buffer2 tickable- , parClockIn = ParClockInL parClockIn- , parClockOut = ParClockInL parClockOut- , lastTime = lastTimeA- , initTime = initTime- } now tagL- return $ tickable- { buffer1 = buffer1 leftTickable- , ticksn = Parallel (ticksn leftTickable) snB- , buffer2 = buffer2 leftTickable- , lastTime = ParallelLastTime (lastTime leftTickable) lastTimeB- }- Right tagR -> do- rightTickable <- tick Tickable- { buffer1 = buffer1 tickable- , ticksn = snB- , buffer2 = buffer2 tickable- , parClockIn = ParClockInR parClockIn- , parClockOut = ParClockInR parClockOut- , lastTime = lastTimeB- , initTime = initTime- } now tagR- return $ tickable- { buffer1 = buffer1 rightTickable- , ticksn = Parallel snA (ticksn rightTickable)- , buffer2 = buffer2 rightTickable- , lastTime = ParallelLastTime lastTimeA (lastTime rightTickable)- }-tick Tickable {} _ _ = error "Impossible pattern in tick"---- TODO It seems wasteful to unwrap and rewrap log(N) Tickables--- (where N is the size of the clock tree) each tick,--- but I have no better idea.--{- | A 'ResamplingBuffer' producing only units.-(Slightly more efficient and direct implementation than the one in 'FRP.Rhine.Timeless'-that additionally unifies the clock types in a way needed for the tick implementation.)--}-trivialResamplingBuffer- :: Monad m => cl- -> ResamplingBuffer m (Out cl) (In cl) () ()-trivialResamplingBuffer _ = go- where- go = ResamplingBuffer {..}- put _ _ = return go- get _ = return ((), go)
src/FRP/Rhine/ResamplingBuffer.hs view
@@ -18,7 +18,6 @@ -- rhine import FRP.Rhine.Clock - -- base import Control.Arrow (second)
src/FRP/Rhine/SN.hs view
@@ -7,13 +7,16 @@ combinators are found in a submodule. -} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.SN where -- rhine import FRP.Rhine.Clock+import FRP.Rhine.Clock.Proxy import FRP.Rhine.ClSF.Core import FRP.Rhine.ResamplingBuffer import FRP.Rhine.Schedule@@ -41,6 +44,9 @@ -- | 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)@@ -52,6 +58,8 @@ -- | 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@@ -61,3 +69,6 @@ => SN m cl1 a b -> SN m cl2 a b -> SN m (ParallelClock m cl1 cl2) 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
@@ -2,12 +2,14 @@ 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.Proxy import FRP.Rhine.ResamplingBuffer.Util import FRP.Rhine.Schedule import FRP.Rhine.SN@@ -61,6 +63,8 @@ -- Note: This is essentially an infix synonym of 'Parallel' (||||) :: ( Monad m, Clock m clL, Clock m clR+ , Clock m (Out clL), Clock m (Out clR)+ , GetClockProxy clL, GetClockProxy clR , Time clL ~ Time clR , Time clL ~ Time (Out clL), Time clL ~ Time (In clL) , Time clR ~ Time (Out clR), Time clR ~ Time (In clR)@@ -75,6 +79,8 @@ -- dependent on which constituent clock has ticked. (++++) :: ( Monad m, Clock m clL, Clock m clR+ , Clock m (Out clL), Clock m (Out clR)+ , GetClockProxy clL, GetClockProxy clR , Time clL ~ Time clR , Time clL ~ Time (Out clL), Time clL ~ Time (In clL) , Time clR ~ Time (Out clR), Time clR ~ Time (In clR)
src/FRP/Rhine/Type.hs view
@@ -3,19 +3,52 @@ A signal network together with a matching clock value. -} +{-# LANGUAGE Arrows #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-} 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 {- | A 'Rhine' consists of a 'SN' together with a clock of matching type 'cl'.+ It is a reactive program, possibly with open inputs and outputs. If the input and output types 'a' and 'b' are both '()', that is, the 'Rhine' is "closed", then it is a standalone reactive program that can be run with the function 'flow'.++Otherwise, one can start the clock and the signal network jointly as a monadic stream function,+using 'eraseClock'. -} data Rhine m cl a b = Rhine { 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.+-}+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+ return $ proc a -> do+ (time, tag) <- runningClock -< ()+ eraseClockSN initTime sn -< (time, tag, a <$ inTag (toClockProxy sn) tag)