{-# LANGUAGE GADTs #-} {-# LANGUAGE RecordWildCards #-} module FRP.Rhine.Reactimation where -- dunai import Data.MonadicStreamFunction -- rhine import FRP.Rhine.Clock import FRP.Rhine.Reactimation.Tick import FRP.Rhine.Schedule import FRP.Rhine.SF {- | An 'SF' together with a clock of matching type 'cl', A 'Rhine' 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'. -} data Rhine m cl a b = Rhine { sf :: SF m cl a b , clock :: cl } -- * Running a Rhine {- | Takes a closed 'Rhine' (with trivial input and output), and runs it indefinitely. All input is created, and all output is consumed by means of side effects in a monad 'm'. Basic usage (synchronous case): @ sensor :: SyncSF MyMonad MyClock () a sensor = arrMSync_ produceData processing :: SyncSF MyMonad MyClock a b processing = ... actuator :: SyncSF MyMonad MyClock b () actuator = arrMSync consumeData mainSF :: SyncSF MyMonad MyClock () () mainSF = sensor >-> processing >-> actuator main :: MyMonad () main = flow $ mainSF @@ clock @ -} -- TODO Can we chuck the constraints into Clock m cl? flow :: ( Monad m, Clock m cl , TimeDomainOf cl ~ TimeDomainOf (Leftmost cl) , TimeDomainOf cl ~ TimeDomainOf (Rightmost cl) ) => Rhine m cl () () -> m () flow Rhine {..} = do (runningClock, initTime) <- startClock clock -- Run the main loop flow' runningClock $ createTickable (trivialResamplingBuffer clock) sf (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'