From 776fb8523ab58b5444e3602f396954e6b914b1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20B=C3=A4renz?= Date: Mon, 10 Jun 2024 15:18:49 +0200 Subject: [PATCH] Replace SN by functions --- .../FRP/Rhine/Reactimation/ClockErasure.hs | 63 ++++++++++++------- .../src/FRP/Rhine/Reactimation/Combinators.hs | 14 +++-- rhine/src/FRP/Rhine/SN/Combinators.hs | 56 +++++------------ rhine/src/FRP/Rhine/Type.hs | 5 +- 4 files changed, 65 insertions(+), 73 deletions(-) diff --git a/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs b/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs index 344e197e..842a64a1 100644 --- a/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs +++ b/rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs @@ -2,6 +2,8 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TupleSections #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} {- | Translate clocked signal processing components to stream functions without explicit clock types. @@ -23,7 +25,7 @@ import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy import FRP.Rhine.Clock.Util import FRP.Rhine.ResamplingBuffer -import FRP.Rhine.SN +import FRP.Rhine.Schedule (In, Out, SequentialClock) {- | Run a clocked signal function as an automaton, accepting the timestamps and tags as explicit inputs. @@ -39,23 +41,22 @@ eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do runReaderS clsf -< (timeInfo, a) {-# INLINE eraseClockClSF #-} -{- | Run a signal network as an automaton. +-- Andras' trick: Encode in the domain +newtype SN m cl a b = SN { getSN :: Reader (Time cl) (Automaton m (Time cl, Tag cl, Maybe a) (Maybe b)) } + +instance GetClockProxy cl => ToClockProxy (SN m cl a b) where + type Cl (SN m cl a b) = cl + +eraseClockSN :: Time cl -> SN m cl a b -> (Automaton m (Time cl, Tag cl, Maybe a) (Maybe b)) +eraseClockSN time = flip runReader time . getSN - 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 -> - 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) +synchronous :: forall cl m a b . ( cl ~ In cl, cl ~ Out cl, Monad m, Clock m cl, GetClockProxy cl) => + ClSF m cl a b -> + SN m cl a b + +synchronous clsf = SN $ reader $ \initialTime -> proc (time, tag, Just a) -> do + b <- eraseClockClSF (getClockProxy @cl) initialTime clsf -< (time, tag, a) returnA -< Just b -- A sequentially composed signal network may either be triggered in its first component, @@ -63,7 +64,19 @@ 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) = +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), Monad m + ) => + SN m clab a b -> + ResamplingBuffer m (Out clab) (In clcd) b c -> + SN m clcd c d -> + SN m (SequentialClock clab clcd) a d +sequential sn1 resBuf sn2 = SN $ reader $ \initialTime -> let proxy1 = toClockProxy sn1 proxy2 = toClockProxy sn2 @@ -81,25 +94,29 @@ eraseClockSN initialTime (Sequential sn1 resBuf sn2) = returnA -< Nothing Right tagR -> do eraseClockSN initialTime sn2 -< (time, tagR, join maybeC) -eraseClockSN initialTime (Parallel snL snR) = proc (time, tag, maybeA) -> do + +parallel snL snR = SN $ reader$ \initialTime -> 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) = + +postcompose sn clsf = SN $ reader $ \initialTime -> 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) = + +precompose clsf sn = SN $ reader $ \initialTime -> 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) = + +feedbackSN ResamplingBuffer {buffer, put, get} sn = SN $ reader $ \initialTime -> let proxy = toClockProxy sn in @@ -119,7 +136,7 @@ eraseClockSN initialTime (Feedback ResamplingBuffer {buffer, put, get} sn) = timeInfo <- genTimeInfo (outProxy proxy) initialTime -< (time, tagOut) buf'' <- arrM $ uncurry $ uncurry put -< ((timeInfo, d), buf') returnA -< (Just b, buf'') -eraseClockSN initialTime (FirstResampling sn buf) = +firstResampling sn buf = SN $ reader $ \initialTime -> let proxy = toClockProxy sn in @@ -132,7 +149,7 @@ eraseClockSN initialTime (FirstResampling sn buf) = _ -> Nothing dMaybe <- mapMaybeS $ eraseClockResBuf (inProxy proxy) (outProxy proxy) initialTime buf -< resBufInput returnA -< (,) <$> bMaybe <*> join dMaybe -{-# INLINE eraseClockSN #-} +{-# INLINE firstResampling #-} {- | Translate a resampling buffer into an automaton. diff --git a/rhine/src/FRP/Rhine/Reactimation/Combinators.hs b/rhine/src/FRP/Rhine/Reactimation/Combinators.hs index c0acc54f..22605a46 100644 --- a/rhine/src/FRP/Rhine/Reactimation/Combinators.hs +++ b/rhine/src/FRP/Rhine/Reactimation/Combinators.hs @@ -22,10 +22,10 @@ import FRP.Rhine.ClSF.Core import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy import FRP.Rhine.ResamplingBuffer -import FRP.Rhine.SN import FRP.Rhine.SN.Combinators import FRP.Rhine.Schedule import FRP.Rhine.Type +import FRP.Rhine.Reactimation.ClockErasure -- * Combinators and syntactic sugar for high-level composition of signal networks. @@ -39,11 +39,14 @@ infix 5 @@ (@@) :: ( cl ~ In cl , cl ~ Out cl + , Monad m + , Clock m cl + , GetClockProxy cl ) => ClSF m cl a b -> cl -> Rhine m cl a b -(@@) = Rhine . Synchronous +(@@) = Rhine . synchronous {-# INLINE (@@) #-} {- | A purely syntactical convenience construction @@ -82,6 +85,7 @@ infixr 1 --> (-->) :: ( Clock m cl1 , Clock m cl2 + , Monad m , Time cl1 ~ Time cl2 , Time (Out cl1) ~ Time cl1 , Time (In cl2) ~ Time cl2 @@ -94,7 +98,7 @@ infixr 1 --> Rhine m cl2 b c -> Rhine m (SequentialClock cl1 cl2) a c RhineAndResamplingBuffer (Rhine sn1 cl1) rb --> (Rhine sn2 cl2) = - Rhine (Sequential sn1 rb sn2) (SequentialClock cl1 cl2) + Rhine (sequential sn1 rb sn2) (SequentialClock cl1 cl2) {- | The combinators for parallel composition allow for the following syntax: @@ -177,7 +181,7 @@ f ^>>@ Rhine sn cl = Rhine (f ^>>> sn) cl -- | Postcompose a 'Rhine' with a 'ClSF'. (@>-^) :: - ( Clock m (Out cl) + ( Clock m (Out cl), GetClockProxy cl, Monad m , Time cl ~ Time (Out cl) ) => Rhine m cl a b -> @@ -187,7 +191,7 @@ Rhine sn cl @>-^ clsf = Rhine (sn >--^ clsf) cl -- | Precompose a 'Rhine' with a 'ClSF'. (^->@) :: - ( Clock m (In cl) + ( Clock m (In cl), GetClockProxy cl, Monad m , Time cl ~ Time (In cl) ) => ClSF m (In cl) a b -> diff --git a/rhine/src/FRP/Rhine/SN/Combinators.hs b/rhine/src/FRP/Rhine/SN/Combinators.hs index c78e9bd7..bd06eca4 100644 --- a/rhine/src/FRP/Rhine/SN/Combinators.hs +++ b/rhine/src/FRP/Rhine/SN/Combinators.hs @@ -11,8 +11,9 @@ import FRP.Rhine.ClSF.Core import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy import FRP.Rhine.ResamplingBuffer.Util -import FRP.Rhine.SN import FRP.Rhine.Schedule +import FRP.Rhine.Reactimation.ClockErasure +import Data.Functor ((<&>)) {- FOURMOLU_DISABLE -} -- | Postcompose a signal network with a pure function. @@ -21,13 +22,7 @@ import FRP.Rhine.Schedule => SN m cl a b -> (b -> c) -> SN m cl a c -Synchronous clsf >>>^ f = Synchronous $ clsf >>^ f -Sequential sn1 rb sn2 >>>^ f = Sequential sn1 rb $ sn2 >>>^ f -Parallel sn1 sn2 >>>^ f = Parallel (sn1 >>>^ f) (sn2 >>>^ f) -Postcompose sn clsf >>>^ f = Postcompose sn $ clsf >>^ f -Precompose clsf sn >>>^ f = Precompose clsf $ sn >>>^ f -Feedback buf sn >>>^ f = Feedback buf $ sn >>>^ first f -firstResampling@(FirstResampling _ _) >>>^ f = Postcompose firstResampling $ arr f +SN {getSN} >>>^ f = SN $ getSN <&> (>>> arr (fmap f)) -- | Precompose a signal network with a pure function. (^>>>) @@ -35,33 +30,28 @@ firstResampling@(FirstResampling _ _) >>>^ f = Postcompose firstResampling $ arr => (a -> b) -> SN m cl b c -> SN m cl a c -f ^>>> Synchronous clsf = Synchronous $ f ^>> clsf -f ^>>> Sequential sn1 rb sn2 = Sequential (f ^>>> sn1) rb sn2 -f ^>>> Parallel sn1 sn2 = Parallel (f ^>>> sn1) (f ^>>> sn2) -f ^>>> Postcompose sn clsf = Postcompose (f ^>>> sn) clsf -f ^>>> Precompose clsf sn = Precompose (f ^>> clsf) sn -f ^>>> Feedback buf sn = Feedback buf $ first f ^>>> sn -f ^>>> firstResampling@(FirstResampling _ _) = Precompose (arr f) firstResampling +f ^>>> SN {getSN} = SN $ getSN <&> (arr (fmap (fmap f)) >>>) -- | Postcompose a signal network with a 'ClSF'. (>--^) - :: ( Clock m (Out cl) + :: ( GetClockProxy cl , Clock m (Out cl) , Time cl ~ Time (Out cl) + , Monad m ) => SN m cl a b -> ClSF m (Out cl) b c -> SN m cl a c -(>--^) = Postcompose +(>--^) = postcompose -- | Precompose a signal network with a 'ClSF'. (^-->) - :: ( Clock m (In cl) + :: ( Clock m (In cl), GetClockProxy cl, Monad m , Time cl ~ Time (In cl) ) => ClSF m (In cl) a b -> SN m cl b c -> SN m cl a c -(^-->) = Precompose +(^-->) = precompose -- | Compose two signal networks on the same clock in data-parallel. -- At one tick of @cl@, both networks are stepped. @@ -70,28 +60,10 @@ f ^>>> firstResampling@(FirstResampling _ _) = Precompose (arr f) firstResamplin => SN m cl a b -> SN m cl c d -> SN m cl (a, c) (b, d) -Synchronous clsf1 **** Synchronous clsf2 = Synchronous $ clsf1 *** clsf2 -Sequential sn11 rb1 sn12 **** Sequential sn21 rb2 sn22 = Sequential sn1 rb sn2 - where - sn1 = sn11 **** sn21 - sn2 = 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 _ _" -Parallel _ _ **** Synchronous _ = error "Impossible pattern: Parallel _ _ **** Synchronous _" -Synchronous _ **** Sequential {} = error "Impossible pattern: Synchronous _ **** Sequential {}" -Sequential {} **** Synchronous _ = error "Impossible pattern: Sequential {} **** Synchronous _" +SN sn1 **** SN sn2 = SN $ do + sn1' <- sn1 + sn2' <- sn2 + pure $ arr (\(time, tag, mac) -> ((time, tag, fst <$> mac), (time, tag, snd <$> mac))) >>> (sn1' *** sn2') >>> arr (\(mb, md) -> (,) <$> mb <*> md) -- | Compose two signal networks on different clocks in clock-parallel. -- At one tick of @ParClock cl1 cl2@, one of the networks is stepped, @@ -109,7 +81,7 @@ Sequential {} **** Synchronous _ = error "Impossible pattern: Sequential {} **** => SN m clL a b -> SN m clR a b -> SN m (ParClock clL clR) a b -(||||) = Parallel +(||||) = parallel -- | Compose two signal networks on different clocks in clock-parallel. -- At one tick of @ParClock cl1 cl2@, one of the networks is stepped, diff --git a/rhine/src/FRP/Rhine/Type.hs b/rhine/src/FRP/Rhine/Type.hs index 291a02c4..c7b7b3e1 100644 --- a/rhine/src/FRP/Rhine/Type.hs +++ b/rhine/src/FRP/Rhine/Type.hs @@ -18,7 +18,6 @@ import FRP.Rhine.Clock import FRP.Rhine.Clock.Proxy import FRP.Rhine.Reactimation.ClockErasure import FRP.Rhine.ResamplingBuffer (ResamplingBuffer) -import FRP.Rhine.SN import FRP.Rhine.Schedule (In, Out) {- | @@ -70,14 +69,14 @@ feedbackRhine :: ( Clock m (In cl) , Clock m (Out cl) , Time (In cl) ~ Time cl - , Time (Out cl) ~ Time cl + , Time (Out cl) ~ Time cl, GetClockProxy cl, Monad m ) => 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 + { sn = feedbackSN buf sn , clock } {-# INLINE feedbackRhine #-}