From 2de85f05300fd2608959cb4e79fd3e920ab48ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20B=C3=A4renz?= Date: Thu, 16 May 2024 13:01:29 +0200 Subject: [PATCH] Simplify initClock --- rhine-gloss/src/FRP/Rhine/Gloss/IO.hs | 4 +- rhine-gloss/src/FRP/Rhine/Gloss/Pure.hs | 5 +- rhine-terminal/src/FRP/Rhine/Terminal.hs | 13 ++-- rhine/src/FRP/Rhine/Clock.hs | 60 ++++--------------- rhine/src/FRP/Rhine/Clock/Except.hs | 37 ++++-------- rhine/src/FRP/Rhine/Clock/FixedStep.hs | 9 +-- rhine/src/FRP/Rhine/Clock/Periodic.hs | 6 +- rhine/src/FRP/Rhine/Clock/Realtime.hs | 35 +++++------ rhine/src/FRP/Rhine/Clock/Realtime/Audio.hs | 24 ++++---- rhine/src/FRP/Rhine/Clock/Realtime/Busy.hs | 7 +-- rhine/src/FRP/Rhine/Clock/Realtime/Event.hs | 7 +-- .../FRP/Rhine/Clock/Realtime/Millisecond.hs | 5 +- rhine/src/FRP/Rhine/Clock/Realtime/Never.hs | 7 +-- rhine/src/FRP/Rhine/Clock/Realtime/Stdin.hs | 7 +-- rhine/src/FRP/Rhine/Clock/Select.hs | 8 +-- rhine/src/FRP/Rhine/Clock/Trivial.hs | 2 +- rhine/src/FRP/Rhine/Clock/Unschedule.hs | 3 +- rhine/src/FRP/Rhine/Clock/Util.hs | 11 ++-- rhine/src/FRP/Rhine/Reactimation.hs | 4 +- .../FRP/Rhine/Reactimation/ClockErasure.hs | 57 +++++++++--------- rhine/src/FRP/Rhine/Schedule.hs | 13 ++-- rhine/src/FRP/Rhine/Type.hs | 11 ++-- rhine/test/Clock/Except.hs | 36 ----------- rhine/test/Schedule.hs | 8 +-- rhine/test/Util.hs | 4 +- 25 files changed, 120 insertions(+), 263 deletions(-) diff --git a/rhine-gloss/src/FRP/Rhine/Gloss/IO.hs b/rhine-gloss/src/FRP/Rhine/Gloss/IO.hs index a59ee7cf..11894b1a 100644 --- a/rhine-gloss/src/FRP/Rhine/Gloss/IO.hs +++ b/rhine-gloss/src/FRP/Rhine/Gloss/IO.hs @@ -126,7 +126,7 @@ data GlossEventClockIO = GlossEventClockIO instance (MonadIO m) => Clock (GlossConcT m) GlossEventClockIO where type Time GlossEventClockIO = Float type Tag GlossEventClockIO = Event - initClock _ = return (constM getEvent, 0) + initClock _ = constM getEvent where getEvent = do GlossEnv {eventVar, timeRef} <- GlossConcT ask @@ -148,7 +148,7 @@ data GlossSimClockIO = GlossSimClockIO instance (MonadIO m) => Clock (GlossConcT m) GlossSimClockIO where type Time GlossSimClockIO = Float type Tag GlossSimClockIO = () - initClock _ = return (constM getTime &&& arr (const ()), 0) + initClock _ = constM getTime &&& arr (const ()) where getTime = GlossConcT $ do GlossEnv {timeVar} <- ask diff --git a/rhine-gloss/src/FRP/Rhine/Gloss/Pure.hs b/rhine-gloss/src/FRP/Rhine/Gloss/Pure.hs index 465ac3a7..5387aecb 100644 --- a/rhine-gloss/src/FRP/Rhine/Gloss/Pure.hs +++ b/rhine-gloss/src/FRP/Rhine/Gloss/Pure.hs @@ -35,7 +35,6 @@ import Control.Monad.Schedule.Class import Control.Monad.Schedule.Yield -- automaton -import Data.Automaton.Trans.Except (performOnFirstSample) import qualified Data.Automaton.Trans.Reader as AutomatonReader import qualified Data.Automaton.Trans.Writer as AutomatonWriter @@ -82,7 +81,7 @@ instance Semigroup GlossClock where instance Clock GlossM GlossClock where type Time GlossClock = Float type Tag GlossClock = Maybe Event - initClock _ = return (constM (GlossM $ yield >> lift ask) >>> (sumS *** Category.id), 0) + initClock _ = constM (GlossM $ yield >> lift ask) >>> (sumS *** Category.id) instance GetClockProxy GlossClock @@ -125,7 +124,7 @@ flowGloss GlossSettings {..} rhine = play display backgroundColor stepsPerSecond (worldAutomaton, Blank) getPic handleEvent simStep where worldAutomaton :: WorldAutomaton - worldAutomaton = AutomatonWriter.runWriterS $ AutomatonReader.runReaderS $ hoistS (runYieldT . unGlossM) $ performOnFirstSample $ eraseClock rhine + worldAutomaton = AutomatonWriter.runWriterS $ AutomatonReader.runReaderS $ hoistS (runYieldT . unGlossM) $ eraseClock rhine stepWith :: (Float, Maybe Event) -> (WorldAutomaton, Picture) -> (WorldAutomaton, Picture) stepWith (diff, eventMaybe) (automaton, _) = let Result automaton' (picture, _) = runIdentity $ stepAutomaton automaton ((diff, eventMaybe), ()) in (automaton', picture) getPic (_, pic) = pic diff --git a/rhine-terminal/src/FRP/Rhine/Terminal.hs b/rhine-terminal/src/FRP/Rhine/Terminal.hs index 475a5422..fdd7c7c8 100644 --- a/rhine-terminal/src/FRP/Rhine/Terminal.hs +++ b/rhine-terminal/src/FRP/Rhine/Terminal.hs @@ -44,15 +44,10 @@ instance (MonadInput m, MonadIO m) => Clock m TerminalEventClock where type Time TerminalEventClock = UTCTime type Tag TerminalEventClock = Either Interrupt Event - initClock TerminalEventClock = do - initialTime <- liftIO getCurrentTime - return - ( constM $ do - event <- awaitEvent - time <- liftIO getCurrentTime - return (time, event) - , initialTime - ) + initClock TerminalEventClock = constM $ do + event <- awaitEvent + time <- liftIO getCurrentTime + return (time, event) instance GetClockProxy TerminalEventClock diff --git a/rhine/src/FRP/Rhine/Clock.hs b/rhine/src/FRP/Rhine/Clock.hs index f9f526b8..62b1b302 100644 --- a/rhine/src/FRP/Rhine/Clock.hs +++ b/rhine/src/FRP/Rhine/Clock.hs @@ -3,7 +3,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {- | @@ -40,13 +39,6 @@ that cause the environment to wait until the specified time is reached. -} type RunningClock m time tag = Automaton m () (time, tag) -{- | -When initialising a clock, the initial time is measured -(typically by means of a side effect), -and a running clock is returned. --} -type RunningClockInit m time tag = m (RunningClock m time tag, time) - {- | Since we want to leverage Haskell's type system to annotate signal networks by their clocks, each clock must be an own type, 'cl'. @@ -69,7 +61,7 @@ class (TimeDomain (Time cl)) => Clock m cl where -- | 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) + RunningClock m (Time cl) (Tag cl) -- * Auxiliary definitions and utilities @@ -112,21 +104,15 @@ type RescalingM m cl time = Time cl -> m time -} type RescalingS m cl time tag = Automaton m (Time cl, Tag cl) (time, tag) -{- | 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@, +{- | Convert an effectful morphism of time domains into a stateful one. + Think of its type as @'RescalingM' m cl time -> 'RescalingS' m cl time tag@, although this type is ambiguous. -} -rescaleMToSInit :: +rescaleMToS :: (Monad m) => (time1 -> m time2) -> - time1 -> - m (Automaton m (time1, tag) (time2, tag), time2) -rescaleMToSInit rescaling time1 = (arrM rescaling *** Category.id,) <$> rescaling time1 + Automaton m (time1, tag) (time2, tag) +rescaleMToS rescaling = arrM rescaling *** Category.id -- ** Applying rescalings to clocks @@ -142,12 +128,7 @@ instance where type Time (RescaledClock cl time) = time type Tag (RescaledClock cl time) = Tag cl - initClock (RescaledClock cl f) = do - (runningClock, initTime) <- initClock cl - return - ( runningClock >>> first (arr f) - , f initTime - ) + initClock (RescaledClock cl f) = initClock cl >>> first (arr f) {- | Instead of a mere function as morphism of time domains, we can transform one time domain into the other with an effectful morphism. @@ -165,13 +146,7 @@ instance where type Time (RescaledClockM m cl time) = time type Tag (RescaledClockM m cl time) = Tag cl - initClock RescaledClockM {..} = do - (runningClock, initTime) <- initClock unscaledClockM - rescaledInitTime <- rescaleM initTime - return - ( runningClock >>> first (arrM rescaleM) - , rescaledInitTime - ) + initClock RescaledClockM {..} = initClock unscaledClockM >>> first (arrM rescaleM) -- | A 'RescaledClock' is trivially a 'RescaledClockM'. rescaledClockToM :: (Monad m) => RescaledClock cl time -> RescaledClockM m cl time @@ -187,7 +162,7 @@ rescaledClockToM RescaledClock {..} = data RescaledClockS m cl time tag = RescaledClockS { unscaledClockS :: cl -- ^ The clock before the rescaling - , rescaleS :: RescalingSInit m cl time tag + , rescaleS :: RescalingS m cl time tag -- ^ The rescaling stream function, and rescaled initial time, -- depending on the initial time before rescaling } @@ -198,13 +173,7 @@ instance where type Time (RescaledClockS m cl time tag) = time type Tag (RescaledClockS m cl time tag) = tag - initClock RescaledClockS {..} = do - (runningClock, initTime) <- initClock unscaledClockS - (rescaling, rescaledInitTime) <- rescaleS initTime - return - ( runningClock >>> rescaling - , rescaledInitTime - ) + initClock RescaledClockS {..} = initClock unscaledClockS >>> rescaleS -- | A 'RescaledClockM' is trivially a 'RescaledClockS'. rescaledClockMToS :: @@ -214,7 +183,7 @@ rescaledClockMToS :: rescaledClockMToS RescaledClockM {..} = RescaledClockS { unscaledClockS = unscaledClockM - , rescaleS = rescaleMToSInit rescaleM + , rescaleS = rescaleMToS rescaleM } -- | A 'RescaledClock' is trivially a 'RescaledClockS'. @@ -236,12 +205,7 @@ instance where type Time (HoistClock m1 m2 cl) = Time cl type Tag (HoistClock m1 m2 cl) = Tag cl - initClock HoistClock {..} = do - (runningClock, initialTime) <- monadMorphism $ initClock unhoistedClock - return - ( hoistS monadMorphism runningClock - , initialTime - ) + initClock HoistClock {..} = hoistS monadMorphism $ initClock unhoistedClock -- | Lift a clock type into a monad transformer. type LiftClock m t cl = HoistClock m (t m) cl diff --git a/rhine/src/FRP/Rhine/Clock/Except.hs b/rhine/src/FRP/Rhine/Clock/Except.hs index f153822c..21112725 100644 --- a/rhine/src/FRP/Rhine/Clock/Except.hs +++ b/rhine/src/FRP/Rhine/Clock/Except.hs @@ -5,7 +5,6 @@ import Control.Arrow import Control.Exception import Control.Exception qualified as Exception import Control.Monad ((<=<)) -import Data.Functor ((<&>)) import Data.Void -- time @@ -50,11 +49,7 @@ instance (Exception e, Clock IO cl, MonadIO eio, MonadError e eio) => Clock eio type Time (ExceptClock cl e) = Time cl type Tag (ExceptClock cl e) = Tag cl - initClock ExceptClock {getExceptClock} = do - ioerror $ - Exception.try $ - initClock getExceptClock - <&> first (hoistS (ioerror . Exception.try)) + initClock ExceptClock {getExceptClock} = hoistS (ioerror . Exception.try) $ initClock getExceptClock where ioerror :: (MonadError e eio, MonadIO eio) => IO (Either e a) -> eio a ioerror = liftEither <=< liftIO @@ -76,17 +71,10 @@ data CatchClock cl1 e cl2 = CatchClock cl1 (e -> cl2) instance (Time cl1 ~ Time cl2, Clock (ExceptT e m) cl1, Clock m cl2, Monad m) => Clock m (CatchClock cl1 e cl2) where type Time (CatchClock cl1 e cl2) = Time cl1 type Tag (CatchClock cl1 e cl2) = Either (Tag cl2) (Tag cl1) - initClock (CatchClock cl1 handler) = do - tryToInit <- runExceptT $ first (>>> arr (second Right)) <$> initClock cl1 - case tryToInit of - Right (runningClock, initTime) -> do - let catchingClock = safely $ do - e <- AutomatonExcept.try runningClock - let cl2 = handler e - (runningClock', _) <- once_ $ initClock cl2 - safe $ runningClock' >>> arr (second Left) - return (catchingClock, initTime) - Left e -> (fmap (first (>>> arr (second Left))) . initClock) $ handler e + initClock (CatchClock cl1 handler) = safely $ do + e <- AutomatonExcept.try $ initClock cl1 >>> arr (second Right) + let cl2 = handler e + safe $ initClock cl2 >>> arr (second Left) instance (GetClockProxy (CatchClock cl1 e cl2)) @@ -134,14 +122,13 @@ data Single m time tag e = Single instance (TimeDomain time, MonadError e m) => Clock m (Single m time tag e) where type Time (Single m time tag e) = time type Tag (Single m time tag e) = tag - initClock Single {singleTag, getTime, exception} = do - initTime <- getTime - let runningClock = hoistS (errorT . runExceptT) $ runAutomatonExcept $ do - step_ (initTime, singleTag) - return exception - errorT :: (MonadError e m) => m (Either e a) -> m a - errorT = (>>= liftEither) - return (runningClock, initTime) + initClock Single {singleTag, getTime, exception} = hoistS (errorT . runExceptT) $ runAutomatonExcept $ do + initTime <- once_ getTime + step_ (initTime, singleTag) + return exception + where + errorT :: (MonadError e m) => m (Either e a) -> m a + errorT = (>>= liftEither) -- * 'DelayException' diff --git a/rhine/src/FRP/Rhine/Clock/FixedStep.hs b/rhine/src/FRP/Rhine/Clock/FixedStep.hs index 551df585..b8b28244 100644 --- a/rhine/src/FRP/Rhine/Clock/FixedStep.hs +++ b/rhine/src/FRP/Rhine/Clock/FixedStep.hs @@ -51,12 +51,9 @@ instance (MonadSchedule m, Monad m) => Clock (ScheduleT Integer m) (FixedStep n) type Tag (FixedStep n) = () initClock cl = let step = stepsize cl - in return - ( arr (const step) - >>> accumulateWith (+) 0 - >>> arrM (\time -> wait step $> (time, ())) - , 0 - ) + in arr (const step) + >>> accumulateWith (+) 0 + >>> arrM (\time -> wait step $> (time, ())) instance GetClockProxy (FixedStep n) diff --git a/rhine/src/FRP/Rhine/Clock/Periodic.hs b/rhine/src/FRP/Rhine/Clock/Periodic.hs index 01ae458c..38469435 100644 --- a/rhine/src/FRP/Rhine/Clock/Periodic.hs +++ b/rhine/src/FRP/Rhine/Clock/Periodic.hs @@ -47,11 +47,7 @@ instance where type Time (Periodic v) = Integer type Tag (Periodic v) = () - initClock cl = - return - ( cycleS (theList cl) >>> withSideEffect wait >>> accumulateWith (+) 0 &&& arr (const ()) - , 0 - ) + initClock cl = cycleS (theList cl) >>> withSideEffect wait >>> accumulateWith (+) 0 &&& arr (const ()) instance GetClockProxy (Periodic v) diff --git a/rhine/src/FRP/Rhine/Clock/Realtime.hs b/rhine/src/FRP/Rhine/Clock/Realtime.hs index efc780e2..c2475311 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime.hs @@ -1,7 +1,7 @@ module FRP.Rhine.Clock.Realtime where -- base -import Control.Arrow (arr) +import Control.Arrow (arr, returnA, (<<<)) import Control.Concurrent (threadDelay) import Control.Monad (guard) import Control.Monad.IO.Class @@ -29,9 +29,9 @@ overwriteUTC :: (MonadIO m) => cl -> UTCClock m cl overwriteUTC cl = RescaledClockS { unscaledClockS = cl - , rescaleS = const $ do - now <- liftIO getCurrentTime - return (arrM $ \(_timePassed, tag) -> (,tag) <$> liftIO getCurrentTime, now) + , rescaleS = proc (_timePassed, tag) -> do + now <- constM $ liftIO getCurrentTime -< () + returnA -< (now, tag) } {- | Rescale a clock to the UTC time domain. @@ -45,9 +45,9 @@ addUTC :: (Real (Time cl), MonadIO m) => cl -> UTCClock m cl addUTC cl = RescaledClockS { unscaledClockS = cl - , rescaleS = const $ do - now <- liftIO getCurrentTime - return (arr $ \(timePassed, tag) -> (addUTCTime (realToFrac timePassed) now, tag), now) + , rescaleS = proc (timePassed, tag) -> do + initialTime <- cacheFirst <<< constM (liftIO getCurrentTime) -< () + returnA -< (addUTCTime (realToFrac timePassed) initialTime, tag) } {- | Like 'UTCClock', but also output in the tag whether and by how much the target realtime was missed. @@ -78,17 +78,14 @@ waitUTC :: (Real (Time cl), MonadIO m, Fractional (Diff (Time cl))) => cl -> Wai waitUTC unscaledClockS = RescaledClockS { unscaledClockS - , rescaleS = \_ -> do - initTime <- liftIO getCurrentTime + , rescaleS = hoistS liftIO $ proc (sinceInitTarget, tag) -> do + beforeSleep <- constM getCurrentTime -< () + initTime <- cacheFirst -< beforeSleep let - runningClock = arrM $ \(sinceInitTarget, tag) -> liftIO $ do - beforeSleep <- getCurrentTime - let - diff :: Rational - diff = toRational $ beforeSleep `diffUTCTime` initTime - remaining = toRational sinceInitTarget - diff - threadDelay $ round $ 1000000 * remaining - now <- getCurrentTime - return (now, (tag, guard (remaining > 0) >> return (fromRational remaining))) - return (runningClock, initTime) + diff :: Rational + diff = toRational $ beforeSleep `diffUTCTime` initTime + remaining = toRational sinceInitTarget - diff + arrM threadDelay -< round $ 1000000 * remaining + now <- constM getCurrentTime -< () + returnA -< (now, (tag, guard (remaining > 0) >> return (fromRational remaining))) } diff --git a/rhine/src/FRP/Rhine/Clock/Realtime/Audio.hs b/rhine/src/FRP/Rhine/Clock/Realtime/Audio.hs index c9ddf733..9c0b0f89 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime/Audio.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime/Audio.hs @@ -102,15 +102,15 @@ instance type Time (AudioClock rate bufferSize) = UTCTime type Tag (AudioClock rate bufferSize) = Maybe Double - initClock audioClock = do + initClock audioClock = let step = picosecondsToDiffTime $ round (10 ^ (12 :: Integer) / theRateNum audioClock :: Double) -- The only sufficiently precise conversion function bufferSize = theBufferSize audioClock - runningClock :: (MonadIO m) => UTCTime -> Maybe Double -> Automaton m () (UTCTime, Maybe Double) - runningClock initialTime maybeWasLate = safely $ do + runningClock :: (MonadIO m) => UTCTime -> Maybe Double -> AutomatonExcept () (UTCTime, Maybe Double) m void + runningClock initialTime maybeWasLate = do bufferFullTime <- try $ proc () -> do n <- count -< () let nextTime = (realToFrac step * fromIntegral (n :: Int)) `addUTCTime` initialTime @@ -120,12 +120,12 @@ instance let lateDiff = currentTime `diffTime` bufferFullTime late = if lateDiff > 0 then Just lateDiff else Nothing - safe $ runningClock bufferFullTime late - initialTime <- liftIO getCurrentTime - return - ( runningClock initialTime Nothing - , initialTime - ) + runningClock bufferFullTime late + in + safely $ do + -- FIXME this is of course a bit inefficient because now we have the full monad in AutomatonExcept. We'd really need something like eolc's >>>= here + initialTime <- once_ $ liftIO getCurrentTime + runningClock initialTime Nothing instance GetClockProxy (AudioClock rate bufferSize) @@ -150,11 +150,7 @@ instance (Monad m, PureAudioClockRate rate) => Clock m (PureAudioClock rate) whe type Time (PureAudioClock rate) = Double type Tag (PureAudioClock rate) = () - initClock audioClock = - return - ( arr (const (1 / thePureRateNum audioClock)) >>> sumS &&& arr (const ()) - , 0 - ) + initClock audioClock = arr (const (1 / thePureRateNum audioClock)) >>> sumS &&& arr (const ()) instance GetClockProxy (PureAudioClock rate) diff --git a/rhine/src/FRP/Rhine/Clock/Realtime/Busy.hs b/rhine/src/FRP/Rhine/Clock/Realtime/Busy.hs index f0ddecce..abc3bd09 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime/Busy.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime/Busy.hs @@ -29,12 +29,7 @@ instance (MonadIO m) => Clock m Busy where type Time Busy = UTCTime type Tag Busy = () - initClock _ = do - initialTime <- liftIO getCurrentTime - return - ( constM (liftIO getCurrentTime) + initClock _ = constM (liftIO getCurrentTime) &&& arr (const ()) - , initialTime - ) instance GetClockProxy Busy diff --git a/rhine/src/FRP/Rhine/Clock/Realtime/Event.hs b/rhine/src/FRP/Rhine/Clock/Realtime/Event.hs index 72172a80..56d6c5dc 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime/Event.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime/Event.hs @@ -150,16 +150,11 @@ instance Semigroup (EventClock event) where instance (MonadIO m) => Clock (EventChanT event m) (EventClock event) where type Time (EventClock event) = UTCTime type Tag (EventClock event) = event - initClock _ = do - initialTime <- liftIO getCurrentTime - return - ( constM $ do + initClock _ = constM $ do chan <- ask event <- liftIO $ readChan chan time <- liftIO getCurrentTime return (time, event) - , initialTime - ) instance GetClockProxy (EventClock event) diff --git a/rhine/src/FRP/Rhine/Clock/Realtime/Millisecond.hs b/rhine/src/FRP/Rhine/Clock/Realtime/Millisecond.hs index f3ccccbf..d974e7e3 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime/Millisecond.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime/Millisecond.hs @@ -9,8 +9,7 @@ Provides a clock that ticks at every multiple of a fixed number of milliseconds. module FRP.Rhine.Clock.Realtime.Millisecond where -- base -import Control.Arrow (arr, first, second, (>>>)) -import Data.Functor ((<&>)) +import Control.Arrow (arr, second, (>>>)) import GHC.TypeLits -- time @@ -40,7 +39,7 @@ newtype Millisecond (n :: Nat) = Millisecond (WaitUTCClock IO (RescaledClock (Un instance Clock IO (Millisecond n) where type Time (Millisecond n) = UTCTime type Tag (Millisecond n) = Maybe Double - initClock (Millisecond cl) = initClock cl <&> first (>>> arr (second snd)) + initClock (Millisecond cl) = initClock cl >>> arr (second snd) instance GetClockProxy (Millisecond n) diff --git a/rhine/src/FRP/Rhine/Clock/Realtime/Never.hs b/rhine/src/FRP/Rhine/Clock/Realtime/Never.hs index a68e1783..e49c62e3 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime/Never.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime/Never.hs @@ -27,11 +27,6 @@ instance (MonadIO m) => Clock m Never where type Time Never = UTCTime type Tag Never = Void - initClock _ = do - initialTime <- liftIO getCurrentTime - return - ( constM (liftIO . forever . threadDelay $ 10 ^ 9) - , initialTime - ) + initClock _ = constM (liftIO . forever . threadDelay $ 10 ^ 9) instance GetClockProxy Never diff --git a/rhine/src/FRP/Rhine/Clock/Realtime/Stdin.hs b/rhine/src/FRP/Rhine/Clock/Realtime/Stdin.hs index 9246f65c..06d0b1a6 100644 --- a/rhine/src/FRP/Rhine/Clock/Realtime/Stdin.hs +++ b/rhine/src/FRP/Rhine/Clock/Realtime/Stdin.hs @@ -36,15 +36,10 @@ instance (MonadIO m) => Clock m StdinClock where type Time StdinClock = UTCTime type Tag StdinClock = Text.Text - initClock _ = do - initialTime <- liftIO getCurrentTime - return - ( constM $ liftIO $ do + initClock _ = constM $ liftIO $ do line <- Text.getLine time <- getCurrentTime return (time, line) - , initialTime - ) instance GetClockProxy StdinClock diff --git a/rhine/src/FRP/Rhine/Clock/Select.hs b/rhine/src/FRP/Rhine/Clock/Select.hs index 63dedbdd..906820d2 100644 --- a/rhine/src/FRP/Rhine/Clock/Select.hs +++ b/rhine/src/FRP/Rhine/Clock/Select.hs @@ -57,13 +57,9 @@ instance (Monoid cl, Semigroup a) => Monoid (SelectClock cl a) where 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 - initClock SelectClock {..} = do - (runningClock, initialTime) <- initClock mainClock - let - runningSelectClock = filterS $ proc _ -> do - (time, tag) <- runningClock -< () + initClock SelectClock {..} = filterS $ proc _ -> do + (time, tag) <- initClock mainClock -< () returnA -< (time,) <$> select tag - return (runningSelectClock, initialTime) instance GetClockProxy (SelectClock cl a) diff --git a/rhine/src/FRP/Rhine/Clock/Trivial.hs b/rhine/src/FRP/Rhine/Clock/Trivial.hs index 8518a303..c99583cd 100644 --- a/rhine/src/FRP/Rhine/Clock/Trivial.hs +++ b/rhine/src/FRP/Rhine/Clock/Trivial.hs @@ -13,6 +13,6 @@ data Trivial = Trivial instance (Monad m) => Clock m Trivial where type Time Trivial = () type Tag Trivial = () - initClock _ = return (arr $ const ((), ()), ()) + initClock _ = arr $ const ((), ()) instance GetClockProxy Trivial diff --git a/rhine/src/FRP/Rhine/Clock/Unschedule.hs b/rhine/src/FRP/Rhine/Clock/Unschedule.hs index 06193188..91a2150b 100644 --- a/rhine/src/FRP/Rhine/Clock/Unschedule.hs +++ b/rhine/src/FRP/Rhine/Clock/Unschedule.hs @@ -5,7 +5,6 @@ module FRP.Rhine.Clock.Unschedule where -- base -import Control.Arrow import Control.Concurrent qualified as Concurrent (yield) import Control.Monad.IO.Class @@ -39,7 +38,7 @@ unyieldClock cl = UnscheduleClock cl $ const $ liftIO Concurrent.yield instance (TimeDomain (Time cl), Clock (ScheduleT (Diff (Time cl)) m) cl, Monad m) => Clock m (UnscheduleClock m cl) where type Tag (UnscheduleClock _ cl) = Tag cl type Time (UnscheduleClock _ cl) = Time cl - initClock UnscheduleClock {scheduleClock, scheduleWait} = run $ first (hoistS run) <$> initClock scheduleClock + initClock UnscheduleClock {scheduleClock, scheduleWait} = hoistS run $ initClock scheduleClock where run :: ScheduleT (Diff (Time cl)) m a -> m a run = runScheduleT scheduleWait diff --git a/rhine/src/FRP/Rhine/Clock/Util.hs b/rhine/src/FRP/Rhine/Clock/Util.hs index 0f95f960..c0cf5cd3 100644 --- a/rhine/src/FRP/Rhine/Clock/Util.hs +++ b/rhine/src/FRP/Rhine/Clock/Util.hs @@ -5,12 +5,13 @@ module FRP.Rhine.Clock.Util where -- base import Control.Arrow +import Data.Maybe (fromMaybe) -- time-domain import Data.TimeDomain -- automaton -import Data.Automaton (Automaton, delay) +import Data.Automaton (Automaton, cacheFirst, delay) -- rhine import FRP.Rhine.Clock @@ -24,14 +25,14 @@ import FRP.Rhine.Clock.Proxy genTimeInfo :: (Monad m, Clock m cl) => ClockProxy cl -> - Time cl -> Automaton m (Time cl, Tag cl) (TimeInfo cl) -genTimeInfo _ initialTime = proc (absolute, tag) -> do - lastTime <- delay initialTime -< absolute +genTimeInfo _ = proc (absolute, tag) -> do + initialTime <- cacheFirst -< absolute + lastTime <- delay Nothing -< Just absolute returnA -< TimeInfo - { sinceLast = absolute `diffTime` lastTime + { sinceLast = absolute `diffTime` fromMaybe initialTime lastTime , sinceInit = absolute `diffTime` initialTime , .. } diff --git a/rhine/src/FRP/Rhine/Reactimation.hs b/rhine/src/FRP/Rhine/Reactimation.hs index 7f07ab3c..d2903e8e 100644 --- a/rhine/src/FRP/Rhine/Reactimation.hs +++ b/rhine/src/FRP/Rhine/Reactimation.hs @@ -54,9 +54,7 @@ flow :: ) => Rhine m cl () () -> m void -flow rhine = do - automaton <- eraseClock rhine - reactimate $ automaton >>> arr (const ()) +flow rhine = reactimate $ eraseClock rhine >>> arr (const ()) {-# INLINE flow #-} {- | Like 'flow', but with the type signature specialized to @m ()@. diff --git a/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs b/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs index 344e197e..e3adc3b1 100644 --- a/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs +++ b/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs @@ -31,11 +31,10 @@ import FRP.Rhine.SN eraseClockClSF :: (Monad m, Clock m cl) => ClockProxy cl -> - Time cl -> ClSF m cl a b -> Automaton m (Time cl, Tag cl, a) b -eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do - timeInfo <- genTimeInfo proxy initialTime -< (time, tag) +eraseClockClSF proxy clsf = proc (time, tag, a) -> do + timeInfo <- genTimeInfo proxy -< (time, tag) runReaderS clsf -< (timeInfo, a) {-# INLINE eraseClockClSF #-} @@ -50,12 +49,11 @@ eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do -} eraseClockSN :: (Monad m, Clock m cl, GetClockProxy cl) => - Time cl -> SN m cl a b -> Automaton 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) +eraseClockSN sn@(Synchronous clsf) = proc (time, tag, Just a) -> do + b <- eraseClockClSF (toClockProxy sn) clsf -< (time, tag, a) returnA -< Just b -- A sequentially composed signal network may either be triggered in its first component, @@ -63,7 +61,7 @@ eraseClockSN initialTime sn@(Synchronous clsf) = proc (time, tag, Just a) -> do -- 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) = +eraseClockSN (Sequential sn1 resBuf sn2) = let proxy1 = toClockProxy sn1 proxy2 = toClockProxy sn2 @@ -71,35 +69,35 @@ eraseClockSN initialTime (Sequential sn1 resBuf sn2) = proc (time, tag, maybeA) -> do resBufIn <- case tag of Left tagL -> do - maybeB <- eraseClockSN initialTime sn1 -< (time, tagL, maybeA) + maybeB <- eraseClockSN 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 + maybeC <- mapMaybeS $ eraseClockResBuf (outProxy proxy1) (inProxy proxy2) 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 + eraseClockSN sn2 -< (time, tagR, join maybeC) +eraseClockSN (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) -eraseClockSN initialTime (Postcompose sn clsf) = + Left tagL -> eraseClockSN snL -< (time, tagL, maybeA) + Right tagR -> eraseClockSN snR -< (time, tagR, maybeA) +eraseClockSN (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 -eraseClockSN initialTime (Precompose clsf sn) = + bMaybe <- eraseClockSN sn -< input + mapMaybeS $ eraseClockClSF (outProxy proxy) clsf -< (time,,) <$> outTag proxy tag <*> bMaybe +eraseClockSN (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) -eraseClockSN initialTime (Feedback ResamplingBuffer {buffer, put, get} sn) = + bMaybe <- mapMaybeS $ eraseClockClSF (inProxy proxy) clsf -< (time,,) <$> inTag proxy tag <*> aMaybe + eraseClockSN sn -< (time, tag, bMaybe) +eraseClockSN (Feedback ResamplingBuffer {buffer, put, get} sn) = let proxy = toClockProxy sn in @@ -108,29 +106,29 @@ eraseClockSN initialTime (Feedback ResamplingBuffer {buffer, put, get} sn) = Nothing -> do returnA -< (Nothing, buf) Just tagIn -> do - timeInfo <- genTimeInfo (inProxy proxy) initialTime -< (time, tagIn) + timeInfo <- genTimeInfo (inProxy proxy) -< (time, tagIn) Result buf' c <- arrM $ uncurry get -< (timeInfo, buf) returnA -< (Just c, buf') - bdMaybe <- eraseClockSN initialTime sn -< (time, tag, (,) <$> aMaybe <*> cMaybe) + bdMaybe <- eraseClockSN 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) + timeInfo <- genTimeInfo (outProxy proxy) -< (time, tagOut) buf'' <- arrM $ uncurry $ uncurry put -< ((timeInfo, d), buf') returnA -< (Just b, buf'') -eraseClockSN initialTime (FirstResampling sn buf) = +eraseClockSN (FirstResampling sn buf) = let proxy = toClockProxy sn in proc (time, tag, acMaybe) -> do - bMaybe <- eraseClockSN initialTime sn -< (time, tag, fst <$> acMaybe) + bMaybe <- eraseClockSN 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 + dMaybe <- mapMaybeS $ eraseClockResBuf (inProxy proxy) (outProxy proxy) buf -< resBufInput returnA -< (,) <$> bMaybe <*> join dMaybe {-# INLINE eraseClockSN #-} @@ -147,17 +145,16 @@ eraseClockResBuf :: ) => ClockProxy cl1 -> ClockProxy cl2 -> - Time cl1 -> ResBuf m cl1 cl2 a b -> Automaton m (Either (Time cl1, Tag cl1, a) (Time cl2, Tag cl2)) (Maybe b) -eraseClockResBuf proxy1 proxy2 initialTime ResamplingBuffer {buffer, put, get} = feedback buffer $ proc (input, resBuf) -> do +eraseClockResBuf proxy1 proxy2 ResamplingBuffer {buffer, put, get} = feedback buffer $ proc (input, resBuf) -> do case input of Left (time1, tag1, a) -> do - timeInfo1 <- genTimeInfo proxy1 initialTime -< (time1, tag1) + timeInfo1 <- genTimeInfo proxy1 -< (time1, tag1) resBuf' <- arrM (uncurry $ uncurry put) -< ((timeInfo1, a), resBuf) returnA -< (Nothing, resBuf') Right (time2, tag2) -> do - timeInfo2 <- genTimeInfo proxy2 initialTime -< (time2, tag2) + timeInfo2 <- genTimeInfo proxy2 -< (time2, tag2) Result resBuf' b <- arrM (uncurry get) -< (timeInfo2, resBuf) returnA -< (Just b, resBuf') {-# INLINE eraseClockResBuf #-} diff --git a/rhine/src/FRP/Rhine/Schedule.hs b/rhine/src/FRP/Rhine/Schedule.hs index 6de3b26d..40777d1d 100644 --- a/rhine/src/FRP/Rhine/Schedule.hs +++ b/rhine/src/FRP/Rhine/Schedule.hs @@ -106,14 +106,11 @@ initSchedule :: ) => cl1 -> cl2 -> - RunningClockInit m (Time cl1) (Either (Tag cl1) (Tag cl2)) -initSchedule cl1 cl2 = do - (runningClock1, initTime) <- initClock cl1 - (runningClock2, _) <- initClock cl2 - return - ( runningSchedule cl1 cl2 runningClock1 runningClock2 - , initTime - ) + RunningClock m (Time cl1) (Either (Tag cl1) (Tag cl2)) +initSchedule cl1 cl2 = let + runningClock1 = initClock cl1 + runningClock2 = initClock cl2 + in runningSchedule cl1 cl2 runningClock1 runningClock2 -- * Composite clocks diff --git a/rhine/src/FRP/Rhine/Type.hs b/rhine/src/FRP/Rhine/Type.hs index 291a02c4..83534835 100644 --- a/rhine/src/FRP/Rhine/Type.hs +++ b/rhine/src/FRP/Rhine/Type.hs @@ -51,13 +51,10 @@ 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 (Automaton 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) + Automaton m a (Maybe b) +eraseClock Rhine {..} = proc a -> do + (time, tag) <- initClock clock -< () + eraseClockSN sn -< (time, tag, a <$ inTag (toClockProxy sn) tag) {-# INLINE eraseClock #-} {- | diff --git a/rhine/test/Clock/Except.hs b/rhine/test/Clock/Except.hs index 0417c3be..cfb2014a 100644 --- a/rhine/test/Clock/Except.hs +++ b/rhine/test/Clock/Except.hs @@ -14,7 +14,6 @@ import Control.Monad.Writer.Class -- transformers -- Replace Strict by CPS when bumping mtl to 2.3 -import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Maybe (MaybeT (..)) import Control.Monad.Trans.Writer.Strict hiding (tell) @@ -90,41 +89,6 @@ catchClockTests = result @?= Right Nothing ] --- ** Clock failing at init - -{- | This clock throws an exception at initialization. - -Useful for testing clock initialization. --} -data FailingClock = FailingClock - -instance (Monad m) => Clock (ExceptT () m) FailingClock where - type Time FailingClock = UTCTime - type Tag FailingClock = () - initClock FailingClock = throwE () - -instance GetClockProxy FailingClock - -type CatchFailingClock = CatchClock FailingClock () Busy - -catchFailingClock :: CatchFailingClock -catchFailingClock = CatchClock FailingClock $ const Busy - -failingClockTests :: TestTree -failingClockTests = - testGroup - "FailingClock" - [ testCase "flow fails immediately" $ do - result <- runExceptT $ flow_ $ clId @@ FailingClock - result @?= Left () - , testCase "CatchClock recovers from failure at init" $ do - let - clsfStops :: ClSF (MaybeT IO) CatchFailingClock () () - clsfStops = catchClSF clId $ constM $ lift empty - result <- runMaybeT $ flow_ $ clsfStops @@ catchFailingClock - result @?= Nothing -- The ClSF stopped the execution, not the clock - ] - -- ** 'DelayException' type DelayedClock = DelayIOError StdinClock (Maybe [Text]) diff --git a/rhine/test/Schedule.hs b/rhine/test/Schedule.hs index bd13f00e..3e2fb81d 100644 --- a/rhine/test/Schedule.hs +++ b/rhine/test/Schedule.hs @@ -20,7 +20,7 @@ import Control.Monad.Schedule.Trans (Schedule, runScheduleT, wait) import Data.Automaton (accumulateWith, constM, embed) -- rhine -import FRP.Rhine.Clock (Clock (initClock), RunningClockInit) +import FRP.Rhine.Clock (Clock (initClock), RunningClock) import FRP.Rhine.Clock.FixedStep (FixedStep (FixedStep)) import FRP.Rhine.Schedule import Util @@ -42,8 +42,8 @@ tests = [ testCase "chronological ticks" $ do let clA = FixedStep @5 clB = FixedStep @3 - (runningClockA, _) = runSchedule (initClock clA :: RunningClockInit (Schedule Integer) Integer ()) - (runningClockB, _) = runSchedule (initClock clB :: RunningClockInit (Schedule Integer) Integer ()) + runningClockA = initClock clA :: RunningClock (Schedule Integer) Integer () + runningClockB = initClock clB :: RunningClock (Schedule Integer) Integer () output = runSchedule $ embed (runningSchedule clA clB runningClockA runningClockB) $ replicate 6 () output @?= [ (3, Right ()) @@ -58,7 +58,7 @@ tests = "ParallelClock" [ testCase "chronological ticks" $ do let - (runningClock, _time) = runSchedule (initClock $ ParallelClock (FixedStep @5) (FixedStep @3) :: RunningClockInit (Schedule Integer) Integer (Either () ())) + runningClock = initClock $ ParallelClock (FixedStep @5) (FixedStep @3) :: RunningClock (Schedule Integer) Integer (Either () ()) output = runSchedule $ embed runningClock $ replicate 6 () output @?= [ (3, Right ()) diff --git a/rhine/test/Util.hs b/rhine/test/Util.hs index 6fece5f4..530ddd11 100644 --- a/rhine/test/Util.hs +++ b/rhine/test/Util.hs @@ -13,9 +13,7 @@ runScheduleRhinePure :: (Clock (Schedule (Diff (Time cl))) cl, GetClockProxy cl) runScheduleRhinePure rhine = runSchedule . runRhine rhine runRhine :: (Clock m cl, GetClockProxy cl, Monad m) => Rhine m cl a b -> [a] -> m [Maybe b] -runRhine rhine input = do - automaton <- eraseClock rhine - embed automaton input +runRhine rhine = embed (eraseClock rhine) -- FIXME Move to monad-schedule runSchedule :: Schedule diff a -> a