Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove SN GADT in favour of inlinable functions #348

Merged
merged 7 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions automaton/src/Data/Automaton.hs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ instance (Monad m) => ArrowChoice (Automaton m) where
right (Automaton (Stateless ma)) = Automaton $! Stateless $! ReaderT $! either (pure . Left) (fmap Right . runReaderT ma)
{-# INLINE right #-}

f ||| g = f +++ g >>> arr untag
where
untag (Left x) = x
untag (Right y) = y
{-# INLINE (|||) #-}

-- | Caution, this can make your program hang. Try to use 'feedback' or 'unfold' where possible, or combine 'loop' with 'delay'.
instance (MonadFix m) => ArrowLoop (Automaton m) where
loop (Automaton (Stateless ma)) = Automaton $! Stateless $! ReaderT (\b -> fst <$> mfix ((. snd) $ ($ b) $ curry $ runReaderT ma))
Expand Down Expand Up @@ -519,11 +525,14 @@ sumS = sumFrom zeroVector
-- | Sum up all inputs so far, initialised at 0.
sumN :: (Monad m, Num a) => Automaton m a a
sumN = arr Sum >>> mappendS >>> arr getSum
{-# INLINE sumN #-}

-- | Count the natural numbers, beginning at 1.
count :: (Num n, Monad m) => Automaton m a n
count = feedback 0 $! arr (\(_, n) -> let n' = n + 1 in (n', n'))
{-# INLINE count #-}

-- | Remembers the last 'Just' value, defaulting to the given initialisation value.
lastS :: (Monad m) => a -> Automaton m (Maybe a) a
lastS a = arr Last >>> mappendS >>> arr (getLast >>> fromMaybe a)
{-# INLINE lastS #-}
2 changes: 2 additions & 0 deletions rhine-gloss/src/FRP/Rhine/Gloss/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ instance (MonadIO m) => Clock (GlossConcT m) GlossEventClockIO where
liftIO $ do
time <- readIORef timeRef
return (time, event)
{-# INLINE initClock #-}

instance GetClockProxy GlossEventClockIO

Expand All @@ -153,6 +154,7 @@ instance (MonadIO m) => Clock (GlossConcT m) GlossSimClockIO where
getTime = GlossConcT $ do
GlossEnv {timeVar} <- ask
lift $ asyncMVar timeVar
{-# INLINE initClock #-}

instance GetClockProxy GlossSimClockIO

Expand Down
1 change: 1 addition & 0 deletions rhine-gloss/src/FRP/Rhine/Gloss/Pure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ 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)
{-# INLINE initClock #-}

instance GetClockProxy GlossClock

Expand Down
1 change: 1 addition & 0 deletions rhine-terminal/src/FRP/Rhine/Terminal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ instance (MonadInput m, MonadIO m) => Clock m TerminalEventClock where
return (time, event)
, initialTime
)
{-# INLINE initClock #-}

instance GetClockProxy TerminalEventClock

Expand Down
5 changes: 5 additions & 0 deletions rhine/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Revision history for rhine

## Upcoming

* Removed `SN` GADT in favour of semantic functions, for a > 100x speedup in some benchmarks
(https://github.com/turion/rhine/pull/348)

## 1.5

* Added `forever` utility for recursion in `ClSFExcept`
Expand Down
1 change: 1 addition & 0 deletions rhine/rhine.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ library
FRP.Rhine.ResamplingBuffer.Util
FRP.Rhine.SN
FRP.Rhine.SN.Combinators
FRP.Rhine.SN.Type
FRP.Rhine.Schedule
FRP.Rhine.Type

Expand Down
4 changes: 4 additions & 0 deletions rhine/src/FRP/Rhine/Clock.hs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ instance
( runningClock >>> first (arr f)
, f initTime
)
{-# INLINE initClock #-}

{- | Instead of a mere function as morphism of time domains,
we can transform one time domain into the other with an effectful morphism.
Expand All @@ -172,6 +173,7 @@ instance
( runningClock >>> first (arrM rescaleM)
, rescaledInitTime
)
{-# INLINE initClock #-}

-- | A 'RescaledClock' is trivially a 'RescaledClockM'.
rescaledClockToM :: (Monad m) => RescaledClock cl time -> RescaledClockM m cl time
Expand Down Expand Up @@ -205,6 +207,7 @@ instance
( runningClock >>> rescaling
, rescaledInitTime
)
{-# INLINE initClock #-}

-- | A 'RescaledClockM' is trivially a 'RescaledClockS'.
rescaledClockMToS ::
Expand Down Expand Up @@ -242,6 +245,7 @@ instance
( hoistS monadMorphism runningClock
, initialTime
)
{-# INLINE initClock #-}

-- | Lift a clock type into a monad transformer.
type LiftClock m t cl = HoistClock m (t m) cl
Expand Down
3 changes: 3 additions & 0 deletions rhine/src/FRP/Rhine/Clock/Except.hs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ instance (Exception e, Clock IO cl, MonadIO eio, MonadError e eio) => Clock eio
where
ioerror :: (MonadError e eio, MonadIO eio) => IO (Either e a) -> eio a
ioerror = liftEither <=< liftIO
{-# INLINE initClock #-}

instance GetClockProxy (ExceptClock cl e)

Expand Down Expand Up @@ -87,6 +88,7 @@ instance (Time cl1 ~ Time cl2, Clock (ExceptT e m) cl1, Clock m cl2, Monad m) =>
safe $ runningClock' >>> arr (second Left)
return (catchingClock, initTime)
Left e -> (fmap (first (>>> arr (second Left))) . initClock) $ handler e
{-# INLINE initClock #-}

instance (GetClockProxy (CatchClock cl1 e cl2))

Expand Down Expand Up @@ -142,6 +144,7 @@ instance (TimeDomain time, MonadError e m) => Clock m (Single m time tag e) wher
errorT :: (MonadError e m) => m (Either e a) -> m a
errorT = (>>= liftEither)
return (runningClock, initTime)
{-# INLINE initClock #-}

-- * 'DelayException'

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/FixedStep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ instance (MonadSchedule m, Monad m) => Clock (ScheduleT Integer m) (FixedStep n)
>>> arrM (\time -> wait step $> (time, ()))
, 0
)
{-# INLINE initClock #-}

instance GetClockProxy (FixedStep n)

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Periodic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ instance
( cycleS (theList cl) >>> withSideEffect wait >>> accumulateWith (+) 0 &&& arr (const ())
, 0
)
{-# INLINE initClock #-}

instance GetClockProxy (Periodic v)

Expand Down
2 changes: 2 additions & 0 deletions rhine/src/FRP/Rhine/Clock/Realtime/Audio.hs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ instance
( runningClock initialTime Nothing
, initialTime
)
{-# INLINE initClock #-}

instance GetClockProxy (AudioClock rate bufferSize)

Expand Down Expand Up @@ -155,6 +156,7 @@ instance (Monad m, PureAudioClockRate rate) => Clock m (PureAudioClock rate) whe
( arr (const (1 / thePureRateNum audioClock)) >>> sumS &&& arr (const ())
, 0
)
{-# INLINE initClock #-}

instance GetClockProxy (PureAudioClock rate)

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Realtime/Busy.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ instance (MonadIO m) => Clock m Busy where
&&& arr (const ())
, initialTime
)
{-# INLINE initClock #-}

instance GetClockProxy Busy
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Realtime/Event.hs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ instance (MonadIO m) => Clock (EventChanT event m) (EventClock event) where
return (time, event)
, initialTime
)
{-# INLINE initClock #-}

instance GetClockProxy (EventClock event)

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Realtime/Millisecond.hs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ 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))
{-# INLINE initClock #-}

instance GetClockProxy (Millisecond n)

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Realtime/Never.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ instance (MonadIO m) => Clock m Never where
( constM (liftIO . forever . threadDelay $ 10 ^ 9)
, initialTime
)
{-# INLINE initClock #-}

instance GetClockProxy Never
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Realtime/Stdin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ instance (MonadIO m) => Clock m StdinClock where
return (time, line)
, initialTime
)
{-# INLINE initClock #-}

instance GetClockProxy StdinClock

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Select.hs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ instance (Monad m, Clock m cl) => Clock m (SelectClock cl a) where
(time, tag) <- runningClock -< ()
returnA -< (time,) <$> select tag
return (runningSelectClock, initialTime)
{-# INLINE initClock #-}

instance GetClockProxy (SelectClock cl a)

Expand Down
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Trivial.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ instance (Monad m) => Clock m Trivial where
type Time Trivial = ()
type Tag Trivial = ()
initClock _ = return (arr $ const ((), ()), ())
{-# INLINE initClock #-}

instance GetClockProxy Trivial
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Unschedule.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ instance (TimeDomain (Time cl), Clock (ScheduleT (Diff (Time cl)) m) cl, Monad m
where
run :: ScheduleT (Diff (Time cl)) m a -> m a
run = runScheduleT scheduleWait
{-# INLINE initClock #-}
1 change: 1 addition & 0 deletions rhine/src/FRP/Rhine/Clock/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ genTimeInfo _ initialTime = proc (absolute, tag) -> do
, sinceInit = absolute `diffTime` initialTime
, ..
}
{-# INLINE genTimeInfo #-}
101 changes: 9 additions & 92 deletions rhine/src/FRP/Rhine/Reactimation/ClockErasure.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{-# LANGUAGE Arrows #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

{- | Translate clocked signal processing components to stream functions without explicit clock types.

Expand All @@ -10,9 +11,6 @@ and is thus not exported from 'FRP.Rhine'.
-}
module FRP.Rhine.Reactimation.ClockErasure where

-- base
import Control.Monad (join)

-- automaton
import Data.Automaton.Trans.Reader
import Data.Stream.Result (Result (..))
Expand All @@ -23,7 +21,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.SN.Type (SN (..))

{- | Run a clocked signal function as an automaton,
accepting the timestamps and tags as explicit inputs.
Expand All @@ -39,99 +37,18 @@ eraseClockClSF proxy initialTime clsf = proc (time, tag, a) -> do
runReaderS clsf -< (timeInfo, a)
{-# INLINE eraseClockClSF #-}

{- | Run a signal network as an automaton.
{- | Remove the signal network type abstraction and reveal the underlying automaton.

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.
* To drive the network, the timestamps and tags of the clock are needed
* Since the input and output clocks are not always guaranteed to tick, the inputs and outputs are 'Maybe'.
-}
eraseClockSN ::
(Monad m, Clock m cl, GetClockProxy cl) =>
-- | Initial time
Time cl ->
-- The original signal network
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)
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)
eraseClockSN initialTime (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) =
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) =
let
proxy = toClockProxy sn
in
feedback buffer $ proc ((time, tag, aMaybe), buf) -> do
(cMaybe, buf') <- case inTag proxy tag of
Nothing -> do
returnA -< (Nothing, buf)
Just tagIn -> do
timeInfo <- genTimeInfo (inProxy proxy) initialTime -< (time, tagIn)
Result buf' c <- arrM $ uncurry get -< (timeInfo, buf)
returnA -< (Just c, buf')
bdMaybe <- eraseClockSN initialTime 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)
buf'' <- arrM $ uncurry $ uncurry put -< ((timeInfo, d), buf')
returnA -< (Just b, buf'')
eraseClockSN initialTime (FirstResampling sn buf) =
let
proxy = toClockProxy sn
in
proc (time, tag, acMaybe) -> do
bMaybe <- eraseClockSN initialTime 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
returnA -< (,) <$> bMaybe <*> join dMaybe
eraseClockSN time = flip runReader time . getSN
{-# INLINE eraseClockSN #-}

{- | Translate a resampling buffer into an automaton.
Expand Down
Loading
Loading