packages feed

bearriver 0.14.10 → 0.14.11

raw patch · 4 files changed

+482/−14 lines, 4 filesdep ~MonadRandomPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: MonadRandom

API changes (from Hackage documentation)

+ FRP.BearRiver.Switches: dkSwitch :: Monad m => SF m a b -> SF m (a, b) (Event c) -> (SF m a b -> c -> SF m a b) -> SF m a b
+ FRP.BearRiver.Switches: dpSwitch :: (Monad m, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, col c) (Event d) -> (col (SF m b c) -> d -> SF m a (col c)) -> SF m a (col c)
+ FRP.BearRiver.Switches: dpSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], [b]) (Event c) -> ([SF m a b] -> c -> SF m [a] [b]) -> SF m [a] [b]
+ FRP.BearRiver.Switches: drSwitch :: Monad m => SF m a b -> SF m (a, Event (SF m a b)) b
+ FRP.BearRiver.Switches: drpSwitch :: (Functor m, Monad m, Functor col, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, Event (col (SF m b c) -> col (SF m b c))) (col c)
+ FRP.BearRiver.Switches: drpSwitchB :: (Functor m, Monad m, Functor col, Traversable col) => col (SF m a b) -> SF m (a, Event (col (SF m a b) -> col (SF m a b))) (col b)
+ FRP.BearRiver.Switches: drpSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], Event ([SF m a b] -> [SF m a b])) [b]
+ FRP.BearRiver.Switches: kSwitch :: Monad m => SF m a b -> SF m (a, b) (Event c) -> (SF m a b -> c -> SF m a b) -> SF m a b
+ FRP.BearRiver.Switches: pSwitch :: (Functor m, Monad m, Traversable col, Functor col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, col c) (Event d) -> (col (SF m b c) -> d -> SF m a (col c)) -> SF m a (col c)
+ FRP.BearRiver.Switches: pSwitchB :: (Functor m, Monad m, Traversable col, Functor col) => col (SF m a b) -> SF m (a, col b) (Event c) -> (col (SF m a b) -> c -> SF m a (col b)) -> SF m a (col b)
+ FRP.BearRiver.Switches: pSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], [b]) (Event c) -> ([SF m a b] -> c -> SF m [a] [b]) -> SF m [a] [b]
+ FRP.BearRiver.Switches: par :: (Functor m, Monad m, Functor col, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m a (col c)
+ FRP.BearRiver.Switches: parZ :: (Functor m, Monad m) => [SF m a b] -> SF m [a] [b]
+ FRP.BearRiver.Switches: rSwitch :: Monad m => SF m a b -> SF m (a, Event (SF m a b)) b
+ FRP.BearRiver.Switches: rpSwitch :: (Functor m, Monad m, Functor col, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, Event (col (SF m b c) -> col (SF m b c))) (col c)
+ FRP.BearRiver.Switches: rpSwitchB :: (Functor m, Monad m, Functor col, Traversable col) => col (SF m a b) -> SF m (a, Event (col (SF m a b) -> col (SF m a b))) (col b)
+ FRP.BearRiver.Switches: rpSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], Event ([SF m a b] -> [SF m a b])) [b]
+ FRP.Yampa: dkSwitch :: Monad m => SF m a b -> SF m (a, b) (Event c) -> (SF m a b -> c -> SF m a b) -> SF m a b
+ FRP.Yampa: dpSwitch :: (Monad m, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, col c) (Event d) -> (col (SF m b c) -> d -> SF m a (col c)) -> SF m a (col c)
+ FRP.Yampa: dpSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], [b]) (Event c) -> ([SF m a b] -> c -> SF m [a] [b]) -> SF m [a] [b]
+ FRP.Yampa: drSwitch :: Monad m => SF m a b -> SF m (a, Event (SF m a b)) b
+ FRP.Yampa: drpSwitch :: (Functor m, Monad m, Functor col, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, Event (col (SF m b c) -> col (SF m b c))) (col c)
+ FRP.Yampa: drpSwitchB :: (Functor m, Monad m, Functor col, Traversable col) => col (SF m a b) -> SF m (a, Event (col (SF m a b) -> col (SF m a b))) (col b)
+ FRP.Yampa: drpSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], Event ([SF m a b] -> [SF m a b])) [b]
+ FRP.Yampa: kSwitch :: Monad m => SF m a b -> SF m (a, b) (Event c) -> (SF m a b -> c -> SF m a b) -> SF m a b
+ FRP.Yampa: loopIntegral :: (MonadFix m, Fractional s, VectorSpace c s) => SF m (a, c) (b, c) -> SF m a b
+ FRP.Yampa: pSwitch :: (Functor m, Monad m, Traversable col, Functor col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, col c) (Event d) -> (col (SF m b c) -> d -> SF m a (col c)) -> SF m a (col c)
+ FRP.Yampa: pSwitchB :: (Functor m, Monad m, Traversable col, Functor col) => col (SF m a b) -> SF m (a, col b) (Event c) -> (col (SF m a b) -> c -> SF m a (col b)) -> SF m a (col b)
+ FRP.Yampa: pSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], [b]) (Event c) -> ([SF m a b] -> c -> SF m [a] [b]) -> SF m [a] [b]
+ FRP.Yampa: par :: (Functor m, Monad m, Functor col, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m a (col c)
+ FRP.Yampa: parZ :: (Functor m, Monad m) => [SF m a b] -> SF m [a] [b]
+ FRP.Yampa: rSwitch :: Monad m => SF m a b -> SF m (a, Event (SF m a b)) b
+ FRP.Yampa: rpSwitch :: (Functor m, Monad m, Functor col, Traversable col) => (forall sf. a -> col sf -> col (b, sf)) -> col (SF m b c) -> SF m (a, Event (col (SF m b c) -> col (SF m b c))) (col c)
+ FRP.Yampa: rpSwitchB :: (Functor m, Monad m, Functor col, Traversable col) => col (SF m a b) -> SF m (a, Event (col (SF m a b) -> col (SF m a b))) (col b)
+ FRP.Yampa: rpSwitchZ :: (Functor m, Monad m) => [SF m a b] -> SF m ([a], Event ([SF m a b] -> [SF m a b])) [b]
- FRP.Yampa: embed :: Monad m => SF m a b -> (a, [(DTime, Maybe a)]) -> m [b]
+ FRP.Yampa: embed :: SF a b -> (a, [(DTime, Maybe a)]) -> [b]
- FRP.Yampa: loopPre :: Monad m => c -> SF m (a, c) (b, c) -> SF m a b
+ FRP.Yampa: loopPre :: MonadFix m => c -> SF m (a, c) (b, c) -> SF m a b

Files

CHANGELOG view
@@ -1,3 +1,10 @@+2024-10-21 Ivan Perez <ivan.perez@keera.co.uk>+        * Version bump (0.14.11) (#442).+        * Offer all definitions from FRP.Yampa.Switches (#426).+        * Publish embed specialized for FRP.Yampa's SF (#439).+        * Increase upper bounds on MonadRandom (#436).+        * Re-export FRP.BearRiver.Loop in FRP.Yampa (#438).+ 2024-08-21 Ivan Perez <ivan.perez@keera.co.uk>         * Version bump (0.14.10) (#430).         * Offer all definitions from FRP.Yampa.Integration (#422).
bearriver.cabal view
@@ -30,7 +30,7 @@ build-type:    Simple  name:          bearriver-version:       0.14.10+version:       0.14.11 author:        Ivan Perez, Manuel Bärenz maintainer:    ivan.perez@keera.co.uk homepage:      https://github.com/ivanperez-keera/dunai@@ -116,7 +116,7 @@    if impl(ghc <= 7.8.4)     build-depends:-      MonadRandom >= 0.2 && < 0.6+      MonadRandom >= 0.2 && < 0.7    if !impl(ghc >= 8.0)     build-depends:
src/FRP/BearRiver/Switches.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP        #-}+{-# LANGUAGE Rank2Types #-} -- The following warning is disabled so that we do not see warnings due to -- using ListT on an MSF to implement parallelism with broadcasting. #if __GLASGOW_HASKELL__ < 800@@ -18,18 +19,80 @@ -- The basic idea of switching is formed by combining a subordinate signal -- function and a signal function continuation parameterised over some initial -- data.+--+-- For example, the most basic switch has the following signature:+--+-- @switch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b@+--+-- which indicates that it has two parameters: a signal function that produces+-- an output and indicates, with an event, when it is time to switch, and a+-- signal function that starts with the residual data left by the first SF in+-- the event and continues onwards.+--+-- Switching occurs, at most, once. If you want something to switch repeatedly,+-- in general, you need to loop, or to switch onto the same signal function+-- again. However, some switches, explained below, are immediate (meaning that+-- the second SF is started at the time of switching). If you use the same SF+-- that originally provoked the switch, you are very likely to fall into an+-- infinite loop. In those cases, the use of 'dSwitch' or '-->' may help.+--+-- Switches vary depending on a number of criteria:+--+-- - /Decoupled/ vs normal switching /(d)/: when an SF is being applied and a+-- different SF needs to be applied next, one question is which one is used for+-- the time in which the switching takes place. In decoupled switching, the old+-- SF is used for the time of switching, and the one SF is only used after that.+-- In normal or instantaneous or coupled switching, the old SF is discarded+-- immediately and a new SF is used for the output already from that point in+-- time.+--+-- - How the switching event is provided /( \/r\/k)/: normally, an 'Event' is+-- used to indicate that a switching must take place. This event can be part of+-- the argument SF (e.g., 'switch'), it can be part of the input (e.g.,+-- 'rSwitch'), or it can be determined by a second argument SF (e.g, 'kSwitch').+--+-- - How many SFs are being handled /( \/p\/par)/: some combinators deal with+-- only one SF, others handle collections, either in the form of a 'Functor' or+-- a list ('[]').+--+-- - How the input is router /(B\/Z\/ )/: when multiple SFs are being combined,+-- a decision needs to be made about how the input is passed to the internal+-- SFs.  In some cases, broadcasting is used to pass the same input to all+-- internal SFs. In others, the input is itself a collection, and each element+-- is passed to one internal SF (i.e., /zipping/). In others, an auxiliary+-- function is used to decide how to route specific inputs to specific SFs in+-- the collection.+--+-- These gives a number of different combinations, some of which make no sense,+-- and also helps determine the expected behaviour of a combinator by looking at+-- its name. For example, 'drpSwitchB' is the decoupled (/d/), recurrent (/r/),+-- parallel (/p/) switch with broadcasting (/B/). module FRP.BearRiver.Switches     (       -- * Basic switching       switch,  dSwitch+    , rSwitch, drSwitch+    , kSwitch, dkSwitch        -- * Parallel composition\/switching (collections)       -- ** With broadcasting     , parB-    , dpSwitchB+    , pSwitchB, dpSwitchB+    , rpSwitchB, drpSwitchB -      -- * Parallel composition\/switching (lists)+      -- ** With helper routing function+    , par+    , pSwitch,  dpSwitch+    , rpSwitch, drpSwitch +      -- * Parallel composition\/switching (lists)+      --+      -- ** With "zip" routing+    , parZ+    , pSwitchZ+    , dpSwitchZ+    , rpSwitchZ+    , drpSwitchZ        -- ** With replication     , parC@@ -40,16 +103,20 @@ #if !MIN_VERSION_base(4,8,0) import Control.Applicative (Applicative (..), (<$>)) #endif-import Data.Traversable as T+import Control.Arrow              (arr, first)+import Control.Monad.Trans.Class  (lift)+import Control.Monad.Trans.Reader (ask, runReaderT)+import Data.Traversable           as T  -- Internal imports (dunai)-import Control.Monad.Trans.MSF                 (local)+import Control.Monad.Trans.MSF                 (local, performOnFirstSample) import Control.Monad.Trans.MSF.List            (sequenceS, widthFirst) import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF))  -- Internal imports-import FRP.BearRiver.Event        (Event (..))-import FRP.BearRiver.InternalCore (SF)+import FRP.BearRiver.Basic        ((>=-))+import FRP.BearRiver.Event        (Event (..), noEventSnd)+import FRP.BearRiver.InternalCore (DTime, SF)  -- * Basic switches @@ -105,10 +172,91 @@                        return (b, ct')     (b, NoEvent) -> return (b, dSwitch ct sfC) +-- | Recurring switch.+--+-- Uses the given SF until an event comes in the input, in which case the SF in+-- the event is turned on, until the next event comes in the input, and so on.+--+-- See <https://wiki.haskell.org/Yampa#Switches> for more information on how+-- this switch works.+rSwitch :: Monad m => SF m a b -> SF m (a, Event (SF m a b)) b+rSwitch sf = switch (first sf) ((noEventSnd >=-) . rSwitch)++-- | Recurring switch with delayed observation.+--+-- Uses the given SF until an event comes in the input, in which case the SF in+-- the event is turned on, until the next event comes in the input, and so on.+--+-- Uses decoupled switch ('dSwitch').+--+-- See <https://wiki.haskell.org/Yampa#Switches> for more information on how+-- this switch works.+drSwitch :: Monad m => SF m a b -> SF m (a, Event (SF m a b)) b+drSwitch sf = dSwitch (first sf) ((noEventSnd >=-) . drSwitch)++-- | Call-with-current-continuation switch.+--+-- Applies the first SF until the input signal and the output signal, when+-- passed to the second SF, produce an event, in which case the original SF and+-- the event are used to build an new SF to switch into.+--+-- See <https://wiki.haskell.org/Yampa#Switches> for more information on how+-- this switch works.+kSwitch :: Monad m+        => SF m a b+        -> SF m (a, b) (Event c)+        -> (SF m a b -> c -> SF m a b)+        -> SF m a b+kSwitch sf10 tfe0 k = MSF tf0+  where+    tf0 a0 = do+      (b0, sf1) <- unMSF sf10 a0+      (me, sfe) <- unMSF tfe0 (a0, b0)+      case me of+        NoEvent  -> return (b0, kSwitch sf1 sfe k)+        Event c0 -> unMSF (k sf10 c0) a0++-- | 'kSwitch' with delayed observation.+--+-- Applies the first SF until the input signal and the output signal, when+-- passed to the second SF, produce an event, in which case the original SF and+-- the event are used to build an new SF to switch into.+--+-- The switch is decoupled ('dSwitch').+--+-- See <https://wiki.haskell.org/Yampa#Switches> for more information on how+-- this switch works.+#if MIN_VERSION_base(4,8,0)+dkSwitch :: Monad m+         => SF m a b+         -> SF m (a, b) (Event c)+         -> (SF m a b -> c -> SF m a b)+         -> SF m a b+#else+dkSwitch :: (Functor m, Monad m)+         => SF m a b+         -> SF m (a, b) (Event c)+         -> (SF m a b -> c -> SF m a b)+         -> SF m a b+#endif+dkSwitch sf1 sfe k = MSF tf -- False+      where+        tf a = do+          (b, sf1')  <- unMSF sf1 a+          (me, sfe') <- unMSF sfe (a, b)+          let sfe'' = case me of+                        NoEvent -> dkSwitch sf1' sfe' k+                        Event c -> performOnFirstSample (snd <$> unMSF (k sf1 c) a)+          return (b, sfe'')+ -- * Parallel composition and switching  -- ** Parallel composition and switching over collections with broadcasting +-- | Tuple a value up with every element of a collection of signal functions.+broadcast :: Functor col => a -> col sf -> col (a, sf)+broadcast a = fmap (\sf -> (a, sf))+ #if MIN_VERSION_base(4,8,0) parB :: Monad m => [SF m a b] -> SF m a [b] #else@@ -123,6 +271,18 @@ -- <https://www.antonycourtney.com/pubs/hw03.pdf> parB = widthFirst . sequenceS +-- | Parallel switch (dynamic collection of signal functions spatially composed+-- in parallel) with broadcasting. See 'pSwitch'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+pSwitchB :: (Functor m, Monad m, Traversable col, Functor col)+         => col (SF m a b)+         -> SF m (a, col b) (Event c)+         -> (col (SF m a b) -> c -> SF m a (col b))+         -> SF m a (col b)+pSwitchB = pSwitch broadcast+ -- | Decoupled parallel switch with broadcasting (dynamic collection of signal -- functions spatially composed in parallel). See 'dpSwitch'. --@@ -143,7 +303,299 @@           NoEvent -> return (dpSwitchB sfs' sfF' sfCs)   return (bs, ct) --- ** Parallel composition over collections+-- | Recurring parallel switch with broadcasting.+--+-- Uses the given collection of SFs, until an event comes in the input, in which+-- case the function in the 'Event' is used to transform the collections of SF+-- to be used with 'rpSwitch' again, until the next event comes in the input,+-- and so on.+--+-- Broadcasting is used to decide which subpart of the input goes to each SF in+-- the collection.+--+-- See 'rpSwitch'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+rpSwitchB :: (Functor m, Monad m, Functor col, Traversable col)+          => col (SF m a b)+          -> SF m (a, Event (col (SF m a b) -> col (SF m a b))) (col b)+rpSwitchB = rpSwitch broadcast++-- | Decoupled recurring parallel switch with broadcasting.+--+-- Uses the given collection of SFs, until an event comes in the input, in which+-- case the function in the 'Event' is used to transform the collections of SF+-- to be used with 'rpSwitch' again, until the next event comes in the input,+-- and so on.+--+-- Broadcasting is used to decide which subpart of the input goes to each SF in+-- the collection.+--+-- This is the decoupled version of 'rpSwitchB'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+drpSwitchB :: (Functor m, Monad m, Functor col, Traversable col)+           => col (SF m a b)+           -> SF m (a, Event (col (SF m a b) -> col (SF m a b))) (col b)+drpSwitchB = drpSwitch broadcast++-- * Parallel composition and switching over collections with general routing++-- | Spatial parallel composition of a signal function collection parameterized+-- on the routing function.+par :: (Functor m, Monad m, Functor col, Traversable col)+    => (forall sf . (a -> col sf -> col (b, sf)))+       -- ^ Determines the input to each signal function in the collection.+       -- IMPORTANT! The routing function MUST preserve the structure of the+       -- signal function collection.+    -> col (SF m b c)+       -- ^ Signal function collection.+    -> SF m a (col c)+par rf sfs0 = MSF tf0+  where+    tf0 a0 = do+      let bsfs0 = rf a0 sfs0+      sfcs0 <- T.mapM (\(b0, sf0) -> (unMSF sf0) b0) bsfs0+      let sfs = fmap snd sfcs0+          cs0 = fmap fst sfcs0+      return (cs0, par rf sfs)++-- | Parallel switch parameterized on the routing function. This is the most+-- general switch from which all other (non-delayed) switches in principle can+-- be derived. The signal function collection is spatially composed in parallel+-- and run until the event signal function has an occurrence. Once the switching+-- event occurs, all signal function are "frozen" and their continuations are+-- passed to the continuation function, along with the event value.+pSwitch :: (Functor m, Monad m, Traversable col, Functor col)+        => (forall sf . (a -> col sf -> col (b, sf)))+           -- ^ Routing function: determines the input to each signal function+           -- in the collection. IMPORTANT! The routing function has an+           -- obligation to preserve the structure of the signal function+           -- collection.+        -> col (SF m b c)+           -- ^ Signal function collection.+        -> SF m (a, col c) (Event d)+           -- ^ Signal function generating the switching event.+        -> (col (SF m b c) -> d -> SF m a (col c))+           -- ^ Continuation to be invoked once event occurs.+        -> SF m a (col c)+pSwitch rf sfs0 sfe0 k = MSF tf0+  where+    tf0 a0 = do+      let bsfs0 = rf a0 sfs0+      sfcs0 <- T.mapM (\(b0, sf0) -> (unMSF sf0) b0) bsfs0+      let sfs   = fmap snd sfcs0+          cs0   = fmap fst sfcs0+      (e, sfe) <- unMSF sfe0 (a0, cs0)+      case e of+        NoEvent  -> return (cs0, pSwitchAux sfs sfe)+        Event d0 -> unMSF (k sfs0 d0) a0++    pSwitchAux sfs sfe = MSF tf+      where+        tf a = do+          let bsfs = rf a sfs+          sfcs' <- T.mapM (\(b, sf) -> (unMSF sf b)) bsfs+          let sfs' = fmap snd sfcs'+              cs   = fmap fst sfcs'+          (e, sfe') <- unMSF sfe (a, cs)+          case e of+            NoEvent -> return (cs, pSwitchAux sfs' sfe')+            Event d -> do dt <- ask+                          unMSF (k (freezeCol sfs dt) d) a++-- | Parallel switch with delayed observation parameterized on the routing+-- function.+--+-- The collection argument to the function invoked on the switching event is of+-- particular interest: it captures the continuations of the signal functions+-- running in the collection maintained by 'dpSwitch' at the time of the+-- switching event, thus making it possible to preserve their state across a+-- switch.  Since the continuations are plain, ordinary signal functions, they+-- can be resumed, discarded, stored, or combined with other signal functions.+dpSwitch :: (Monad m, Traversable col)+         => (forall sf. (a -> col sf -> col (b, sf)))+            -- ^ Routing function. Its purpose is to pair up each running signal+            -- function in the collection maintained by 'dpSwitch' with the+            -- input it is going to see at each point in time. All the routing+            -- function can do is specify how the input is distributed.+         -> col (SF m b c)+            -- ^ Initial collection of signal functions.+         -> SF m (a, col c) (Event d)+            -- ^ Signal function that observes the external input signal and the+            -- output signals from the collection in order to produce a+            -- switching event.+         -> (col (SF m b c) -> d -> SF m a (col c))+            -- ^ The fourth argument is a function that is invoked when the+            -- switching event occurs, yielding a new signal function to switch+            -- into based on the collection of signal functions previously+            -- running and the value carried by the switching event. This allows+            -- the collection to be updated and then switched back in, typically+            -- by employing 'dpSwitch' again.+         -> SF m a (col c)+dpSwitch rf sfs sfF sfCs = MSF $ \a -> do+  let bsfs = rf a sfs+  res <- T.mapM (\(b, sf) -> unMSF sf b) bsfs+  let cs   = fmap fst res+      sfs' = fmap snd res+  (e, sfF') <- unMSF sfF (a, cs)+  let ct = case e of+          Event d -> sfCs sfs' d+          NoEvent -> dpSwitch rf sfs' sfF' sfCs+  return (cs, ct)++-- | Recurring parallel switch parameterized on the routing function.+--+-- Uses the given collection of SFs, until an event comes in the input, in which+-- case the function in the 'Event' is used to transform the collections of SF+-- to be used with 'rpSwitch' again, until the next event comes in the input,+-- and so on.+--+-- The routing function is used to decide which subpart of the input goes to+-- each SF in the collection.+--+-- This is the parallel version of 'rSwitch'.+rpSwitch :: (Functor m, Monad m, Functor col, Traversable col)+         => (forall sf . (a -> col sf -> col (b, sf)))+            -- ^ Routing function: determines the input to each signal function+            -- in the collection. IMPORTANT! The routing function has an+            -- obligation to preserve the structure of the signal function+            -- collection.+         -> col (SF m b c)+            -- ^ Initial signal function collection.+         -> SF m (a, Event (col (SF m b c) -> col (SF m b c))) (col c)+rpSwitch rf sfs =+  pSwitch (rf . fst) sfs (arr (snd . fst)) $ \sfs' f ->+  noEventSnd >=- rpSwitch rf (f sfs')++-- | Recurring parallel switch with delayed observation parameterized on the+-- routing function.+--+-- Uses the given collection of SFs, until an event comes in the input, in which+-- case the function in the 'Event' is used to transform the collections of SF+-- to be used with 'rpSwitch' again, until the next event comes in the input,+-- and so on.+--+-- The routing function is used to decide which subpart of the input goes to+-- each SF in the collection.+--+-- This is the parallel version of 'drSwitch'.+drpSwitch :: (Functor m, Monad m, Functor col, Traversable col)+          => (forall sf . (a -> col sf -> col (b, sf)))+             -- ^ Routing function: determines the input to each signal function+             -- in the collection. IMPORTANT! The routing function has an+             -- obligation to preserve the structure of the signal function+             -- collection.+          -> col (SF m b c)+             -- ^ Initial signal function collection.+          -> SF m (a, Event (col (SF m b c) -> col (SF m b c))) (col c)+drpSwitch rf sfs =+  dpSwitch (rf . fst) sfs (arr (snd . fst)) $ \sfs' f ->+    noEventSnd >=- drpSwitch rf (f sfs')++-- * Parallel composition/switchers with "zip" routing++-- | Parallel composition of a list of SFs.+--+-- Given a list of SFs, returns an SF that takes a list of inputs, applies each+-- SF to each input in order, and returns the SFs' outputs.+--+-- >>> embed (parZ [arr (+1), arr (+2)]) (deltaEncode 0.1 [[0, 0], [1, 1]])+-- [[1,2],[2,3]]+--+-- If there are more SFs than inputs, an exception is thrown.+--+-- >>> embed (parZ [arr (+1), arr (+1), arr (+2)]) (deltaEncode 0.1 [[0, 0], [1, 1]])+-- [[1,1,*** Exception: FRP.Yampa.Switches.parZ: Input list too short.+--+-- If there are more inputs than SFs, the unused inputs are ignored.+--+-- >>> embed (parZ [arr (+1)]) (deltaEncode 0.1 [[0, 0], [1, 1]])+-- [[1],[2]]+parZ :: (Functor m, Monad m) => [SF m a b] -> SF m [a] [b]+parZ = par (safeZip "parZ")++-- | Parallel switch (dynamic collection of signal functions spatially composed+-- in parallel). See 'pSwitch'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+pSwitchZ :: (Functor m, Monad m)+         => [SF m a b]+         -> SF m ([a], [b]) (Event c)+         -> ([SF m a b] -> c -> SF m [a] [b])+         -> SF m [a] [b]+pSwitchZ = pSwitch (safeZip "pSwitchZ")++-- | Decoupled parallel switch with broadcasting (dynamic collection of signal+-- functions spatially composed in parallel). See 'dpSwitch'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+dpSwitchZ :: (Functor m, Monad m)+          => [SF m a b]+          -> SF m ([a], [b]) (Event c)+          -> ([SF m a b] -> c -> SF m [a] [b])+          -> SF m [a] [b]+dpSwitchZ = dpSwitch (safeZip "dpSwitchZ")++-- | Recurring parallel switch with "zip" routing.+--+-- Uses the given list of SFs, until an event comes in the input, in which case+-- the function in the 'Event' is used to transform the list of SF to be used+-- with 'rpSwitchZ' again, until the next event comes in the input, and so on.+--+-- Zip routing is used to decide which subpart of the input goes to each SF in+-- the list.+--+-- See 'rpSwitch'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+rpSwitchZ :: (Functor m, Monad m)+          => [SF m a b] -> SF m ([a], Event ([SF m a b] -> [SF m a b])) [b]+rpSwitchZ = rpSwitch (safeZip "rpSwitchZ")++-- | Decoupled recurring parallel switch with "zip" routing.+--+-- Uses the given list of SFs, until an event comes in the input, in which case+-- the function in the 'Event' is used to transform the list of SF to be used+-- with 'rpSwitchZ' again, until the next event comes in the input, and so on.+--+-- Zip routing is used to decide which subpart of the input goes to each SF in+-- the list.+--+-- See 'rpSwitchZ' and 'drpSwitch'.+--+-- For more information on how parallel composition works, check+-- <https://www.antonycourtney.com/pubs/hw03.pdf>+drpSwitchZ :: (Functor m, Monad m)+           => [SF m a b] -> SF m ([a], Event ([SF m a b] -> [SF m a b])) [b]+drpSwitchZ = drpSwitch (safeZip "drpSwitchZ")++-- | Zip two lists.+--+-- PRE: The first list is not shorter than the second.+safeZip :: String -> [a] -> [b] -> [(a, b)]+safeZip fn = safeZip'+  where+    safeZip' :: [a] -> [b] -> [(a, b)]+    safeZip' _      []     = []+    safeZip' (a:as) (b:bs) = (a, b) : safeZip' as bs+    safeZip' _      _      =+      error $ "FRP.BearRiver.Switches: " ++ fn ++ ": Input list too short."++-- Freezes a "running" signal function, i.e., turns it into a continuation in+-- the form of a plain signal function.+freeze :: Monad m => SF m a b -> DTime -> SF m a b+freeze sf dt = MSF $ \a ->+  lift $ runReaderT (unMSF sf a) dt++freezeCol :: (Monad m, Functor col)+          => col (SF m a b) -> DTime -> col (SF m a b)+freezeCol sfs dt = fmap (`freeze` dt) sfs  -- | Apply an SF to every element of a list. --
src/FRP/Yampa.hs view
@@ -3,14 +3,15 @@ --              (c) Ivan Perez and Manuel Baerenz, 2016-2018 -- License    : BSD3 -- Maintainer : ivan.perez@keera.co.uk-module FRP.Yampa (module X, SF, FutureSF) where+module FRP.Yampa (module X, SF, FutureSF, embed) where  -- External imports-import Data.Functor.Identity (Identity)+import Data.Functor.Identity (Identity, runIdentity)  -- Internal imports-import           FRP.BearRiver as X hiding (FutureSF, SF)-import qualified FRP.BearRiver as BR+import           FRP.BearRiver      as X hiding (FutureSF, SF, embed, loopPre)+import qualified FRP.BearRiver      as BR+import           FRP.BearRiver.Loop as X  -- | Signal function (conceptually, a function between signals that respects -- causality).@@ -21,3 +22,11 @@ -- -- A future signal is a signal that is only defined for positive times. type FutureSF = BR.SF Identity++-- | Given a signal function and a pair with an initial input sample for the+-- input signal, and a list of sampling times, possibly with new input samples+-- at those times, it produces a list of output samples.+--+-- This is a simplified, purely-functional version of 'reactimate'.+embed :: SF a b -> (a, [(DTime, Maybe a)]) -> [b]+embed sf = runIdentity . BR.embed sf