Skip to content

Commit

Permalink
Replace SN by functions
Browse files Browse the repository at this point in the history
  • Loading branch information
turion committed Aug 8, 2024
1 parent 837d3e7 commit 776fb85
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 73 deletions.
63 changes: 40 additions & 23 deletions rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -39,31 +41,42 @@ 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,
-- 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) =
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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down
14 changes: 9 additions & 5 deletions rhine/src/FRP/Rhine/Reactimation/Combinators.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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 ->
Expand All @@ -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 ->
Expand Down
56 changes: 14 additions & 42 deletions rhine/src/FRP/Rhine/SN/Combinators.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -21,47 +22,36 @@ 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.
(^>>>)
:: Monad m
=> (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.
Expand All @@ -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,
Expand All @@ -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,
Expand Down
5 changes: 2 additions & 3 deletions rhine/src/FRP/Rhine/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)

{- |
Expand Down Expand Up @@ -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 #-}

0 comments on commit 776fb85

Please sign in to comment.