packages feed

bearriver 0.10.4.6 → 0.13.1

raw patch · 3 files changed

+424/−134 lines, 3 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ FRP.BearRiver: (-:>) :: Monad m => b -> SF m a b -> SF m a b
+ FRP.BearRiver: (>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b
+ FRP.BearRiver: accumBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) (Event b)
+ FRP.BearRiver: afterEach :: Monad m => [(Time, b)] -> SF m a (Event b)
+ FRP.BearRiver: afterEachCat :: Monad m => [(Time, b)] -> SF m a (Event [b])
+ FRP.BearRiver: arrEPrim :: Monad m => (Event a -> b) -> SF m (Event a) b
+ FRP.BearRiver: arrPrim :: Monad m => (a -> b) -> SF m a b
+ FRP.BearRiver: attach :: Event a -> b -> Event (a, b)
+ FRP.BearRiver: catEvents :: [Event a] -> Event [a]
+ FRP.BearRiver: dropEvents :: Monad m => Int -> SF m (Event a) (Event a)
+ FRP.BearRiver: edgeJust :: Monad m => SF m (Maybe a) (Event a)
+ FRP.BearRiver: edgeTag :: Monad m => a -> SF m Bool (Event a)
+ FRP.BearRiver: evalAt :: SF Identity a b -> DTime -> a -> (b, SF Identity a b)
+ FRP.BearRiver: evalAtZero :: SF Identity a b -> a -> (b, SF Identity a b)
+ FRP.BearRiver: evalFuture :: SF Identity a b -> a -> DTime -> (b, SF Identity a b)
+ FRP.BearRiver: filterE :: (a -> Bool) -> Event a -> Event a
+ FRP.BearRiver: gate :: Event a -> Bool -> Event a
+ FRP.BearRiver: iEdge :: Monad m => Bool -> SF m Bool (Event ())
+ FRP.BearRiver: infixr 0 >=-
+ FRP.BearRiver: initially :: Monad m => a -> SF m a a
+ FRP.BearRiver: isNoEvent :: () => Event a -> Bool
+ FRP.BearRiver: joinE :: Event a -> Event b -> Event (a, b)
+ FRP.BearRiver: mapEventS :: Monad m => MSF m a b -> MSF m (Event a) (Event b)
+ FRP.BearRiver: mapFilterE :: (a -> Maybe b) -> Event a -> Event b
+ FRP.BearRiver: mapMerge :: (a -> c) -> (b -> c) -> (a -> b -> c) -> Event a -> Event b -> Event c
+ FRP.BearRiver: merge :: Event a -> Event a -> Event a
+ FRP.BearRiver: mergeEvents :: [Event a] -> Event a
+ FRP.BearRiver: noEventFst :: (Event a, b) -> (Event c, b)
+ FRP.BearRiver: noEventSnd :: (a, Event b) -> (a, Event c)
+ FRP.BearRiver: splitE :: Event (a, b) -> (Event a, Event b)
+ FRP.BearRiver: sscan :: Monad m => (b -> a -> b) -> b -> SF m a b
+ FRP.BearRiver: sscanPrim :: Monad m => (c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
+ FRP.BearRiver: tagWith :: b -> Event a -> Event b
+ FRP.Yampa: (-:>) :: Monad m => b -> SF m a b -> SF m a b
+ FRP.Yampa: (>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b
+ FRP.Yampa: accumBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) (Event b)
+ FRP.Yampa: afterEach :: Monad m => [(Time, b)] -> SF m a (Event b)
+ FRP.Yampa: afterEachCat :: Monad m => [(Time, b)] -> SF m a (Event [b])
+ FRP.Yampa: arrEPrim :: Monad m => (Event a -> b) -> SF m (Event a) b
+ FRP.Yampa: arrPrim :: Monad m => (a -> b) -> SF m a b
+ FRP.Yampa: attach :: Event a -> b -> Event (a, b)
+ FRP.Yampa: catEvents :: [Event a] -> Event [a]
+ FRP.Yampa: dropEvents :: Monad m => Int -> SF m (Event a) (Event a)
+ FRP.Yampa: edgeJust :: Monad m => SF m (Maybe a) (Event a)
+ FRP.Yampa: edgeTag :: Monad m => a -> SF m Bool (Event a)
+ FRP.Yampa: evalAt :: SF Identity a b -> DTime -> a -> (b, SF Identity a b)
+ FRP.Yampa: evalAtZero :: SF Identity a b -> a -> (b, SF Identity a b)
+ FRP.Yampa: evalFuture :: SF Identity a b -> a -> DTime -> (b, SF Identity a b)
+ FRP.Yampa: filterE :: (a -> Bool) -> Event a -> Event a
+ FRP.Yampa: gate :: Event a -> Bool -> Event a
+ FRP.Yampa: iEdge :: Monad m => Bool -> SF m Bool (Event ())
+ FRP.Yampa: infixr 0 >=-
+ FRP.Yampa: initially :: Monad m => a -> SF m a a
+ FRP.Yampa: isNoEvent :: () => Event a -> Bool
+ FRP.Yampa: joinE :: Event a -> Event b -> Event (a, b)
+ FRP.Yampa: mapEventS :: Monad m => MSF m a b -> MSF m (Event a) (Event b)
+ FRP.Yampa: mapFilterE :: (a -> Maybe b) -> Event a -> Event b
+ FRP.Yampa: mapMerge :: (a -> c) -> (b -> c) -> (a -> b -> c) -> Event a -> Event b -> Event c
+ FRP.Yampa: merge :: Event a -> Event a -> Event a
+ FRP.Yampa: mergeEvents :: [Event a] -> Event a
+ FRP.Yampa: noEventFst :: (Event a, b) -> (Event c, b)
+ FRP.Yampa: noEventSnd :: (a, Event b) -> (a, Event c)
+ FRP.Yampa: splitE :: Event (a, b) -> (Event a, Event b)
+ FRP.Yampa: sscan :: Monad m => (b -> a -> b) -> b -> SF m a b
+ FRP.Yampa: sscanPrim :: Monad m => (c -> a -> Maybe (c, b)) -> c -> b -> SF m a b
+ FRP.Yampa: tagWith :: b -> Event a -> Event b
+ FRP.Yampa: type FutureSF = SF Identity
- FRP.BearRiver: localTime :: Monad m => SF m () Time
+ FRP.BearRiver: localTime :: Monad m => SF m a Time
- FRP.BearRiver: maybeToEvent :: () => Maybe a -> Event a
+ FRP.BearRiver: maybeToEvent :: Maybe a -> Event a
- FRP.BearRiver: repeatedly :: Monad m => (a -> a) -> a -> MSF m () a
+ FRP.BearRiver: repeatedly :: Monad m => Time -> b -> SF m a (Event b)
- FRP.BearRiver: time :: Monad m => SF m () Time
+ FRP.BearRiver: time :: Monad m => SF m a Time
- FRP.Yampa: localTime :: Monad m => SF m () Time
+ FRP.Yampa: localTime :: Monad m => SF m a Time
- FRP.Yampa: maybeToEvent :: () => Maybe a -> Event a
+ FRP.Yampa: maybeToEvent :: Maybe a -> Event a
- FRP.Yampa: repeatedly :: Monad m => (a -> a) -> a -> MSF m () a
+ FRP.Yampa: repeatedly :: Monad m => Time -> b -> SF m a (Event b)
- FRP.Yampa: time :: Monad m => SF m () Time
+ FRP.Yampa: time :: Monad m => SF m a Time

Files

bearriver.cabal view
@@ -1,5 +1,5 @@ name:                bearriver-version:             0.10.4.6+version:             0.13.1 synopsis:            A replacement of Yampa based on Monadic Stream Functions. description:         A Yampa replacement built using Dunai. homepage:            keera.co.uk
src/FRP/BearRiver.hs view
@@ -19,111 +19,183 @@ import           Control.Monad.Random import           Control.Monad.Trans.Maybe import           Control.Monad.Trans.MSF                        hiding (switch)-import           Control.Monad.Trans.MSF.Except                 as MSF hiding (switch)-import           Control.Monad.Trans.MSF.List                   (widthFirst, sequenceS)+import qualified Control.Monad.Trans.MSF                        as MSF+import           Control.Monad.Trans.MSF.Except                 as MSF hiding+                                                                        (switch)+import           Control.Monad.Trans.MSF.List                   (sequenceS,+                                                                 widthFirst) import           Control.Monad.Trans.MSF.Random import           Data.Functor.Identity import           Data.Maybe-import           Data.MonadicStreamFunction.InternalCore import           Data.MonadicStreamFunction                     as X hiding (reactimate,+                                                                      repeatedly,                                                                       sum,                                                                       switch,                                                                       trace) import qualified Data.MonadicStreamFunction                     as MSF-import qualified Control.Monad.Trans.MSF                        as MSF import           Data.MonadicStreamFunction.Instances.ArrowLoop+import           Data.MonadicStreamFunction.InternalCore import           Data.Traversable                               as T import           FRP.Yampa.VectorSpace                          as X +infixr 0 -->, -:>, >--, >=-++-- * Basic definitions+ type Time  = Double+ type DTime = Double  type SF m        = MSF (ClockInfo m)+ type ClockInfo m = ReaderT DTime m +data Event a = Event a | NoEvent+ deriving (Eq, Show)++-- ** Lifting+arrPrim :: Monad m => (a -> b) -> SF m a b+arrPrim = arr++arrEPrim :: Monad m => (Event a -> b) -> SF m (Event a) b+arrEPrim = arr++-- * Signal functions++-- ** Basic signal functions+ identity :: Monad m => SF m a a identity = Category.id  constant :: Monad m => b -> SF m a b constant = arr . const ---- * Continuous time+localTime :: Monad m => SF m a Time+localTime = constant 1.0 >>> integral -time :: Monad m => SF m () Time+time :: Monad m => SF m a Time time = localTime -localTime :: Monad m => SF m () Time-localTime = constant 1.0 >>> integral+-- ** Initialization -integral :: (Monad m, VectorSpace a s) => SF m a a-integral = integralFrom zeroVector+-- | Initialization operator (cf. Lustre/Lucid Synchrone).+--+-- The output at time zero is the first argument, and from+-- that point on it behaves like the signal function passed as+-- second argument.+(-->) :: Monad m => b -> SF m a b -> SF m a b+b0 --> sf = sf >>> replaceOnce b0 -integralFrom :: (Monad m, VectorSpace a s) => a -> SF m a a-integralFrom a0 = proc a -> do-  dt <- constM ask         -< ()-  accumulateWith (^+^) a0 -< realToFrac dt *^ a+-- | Output pre-insert operator.+--+-- Insert a sample in the output, and from that point on, behave+-- like the given sf.+(-:>) :: Monad m => b -> SF m a b -> SF m a b+b -:> sf = iPost b sf -derivative :: (Monad m, VectorSpace a s) => SF m a a-derivative = derivativeFrom zeroVector+-- | Input initialization operator.+--+-- The input at time zero is the first argument, and from+-- that point on it behaves like the signal function passed as+-- second argument.+(>--) :: Monad m => a -> SF m a b -> SF m a b+a0 >-- sf = replaceOnce a0 >>> sf -derivativeFrom :: (Monad m, VectorSpace a s) => a -> SF m a a-derivativeFrom a0 = proc a -> do-  dt   <- constM ask   -< ()-  aOld <- MSF.iPre a0 -< a-  returnA             -< (a ^-^ aOld) ^/ realToFrac dt+(>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b+f >=- sf = MSF $ \a -> do+  (b, sf') <- unMSF sf (f a)+  return (b, sf') --- * Events+initially :: Monad m => a -> SF m a a+initially = (--> identity) -data Event a = Event a | NoEvent- deriving (Eq, Show)+-- * Simple, stateful signal processing+sscan :: Monad m => (b -> a -> b) -> b -> SF m a b+sscan f b_init = feedback b_init u+  where u = undefined -- (arr f >>^ dup) -instance Functor Event where-  fmap f NoEvent   = NoEvent-  fmap f (Event c) = Event (f c)+sscanPrim :: Monad m => (c -> a -> Maybe (c, b)) -> c -> b -> SF m a b+sscanPrim f c_init b_init = MSF $ \a -> do+  let o = f c_init a+  case o of+    Nothing       -> return (b_init, sscanPrim f c_init b_init)+    Just (c', b') -> return (b',     sscanPrim f c' b') -instance Applicative Event where-  pure = Event -  Event f <*> Event x = Event (f x)-  _       <*> _       = NoEvent+-- | Event source that never occurs.+never :: Monad m => SF m a (Event b)+never = constant NoEvent -noEvent :: Event a-noEvent = NoEvent+-- | Event source with a single occurrence at time 0. The value of the event+-- is given by the function argument.+now :: Monad m => b -> SF m a (Event b)+now b0 = Event b0 --> never -event :: a -> (b -> a) -> Event b -> a-event _ f (Event x) = f x-event x _ NoEvent   = x+after :: Monad m+      => Time -- ^ The time /q/ after which the event should be produced+      -> b    -- ^ Value to produce at that time+      -> SF m a (Event b)+after q x = feedback q go+ where go = MSF $ \(_, t) -> do+              dt <- ask+              let t' = t - dt+                  e  = if t > 0 && t' < 0 then Event x else NoEvent+                  ct = if t' < 0 then constant (NoEvent, t') else go+              return ((e, t'), ct) -fromEvent (Event x) = x-fromEvent _         = error "fromEvent NoEvent"+repeatedly :: Monad m => Time -> b -> SF m a (Event b)+repeatedly q x+    | q > 0     = afterEach qxs+    | otherwise = error "bearriver: repeatedly: Non-positive period."+  where+    qxs = (q,x):qxs -isEvent (Event _) = True-isEvent _         = False+-- | Event source with consecutive occurrences at the given intervals.+-- Should more than one event be scheduled to occur in any sampling interval,+-- only the first will in fact occur to avoid an event backlog. -tag :: Event a -> b -> Event b-tag NoEvent   _ = NoEvent-tag (Event _) b = Event b+-- After all, after, repeatedly etc. are defined in terms of afterEach.+afterEach :: Monad m => [(Time,b)] -> SF m a (Event b)+afterEach qxs = afterEachCat qxs >>> arr (fmap head) -mergeBy :: (a -> a -> a) -> Event a -> Event a -> Event a-mergeBy _       NoEvent      NoEvent      = NoEvent-mergeBy _       le@(Event _) NoEvent      = le-mergeBy _       NoEvent      re@(Event _) = re-mergeBy resolve (Event l)    (Event r)    = Event (resolve l r)+-- | Event source with consecutive occurrences at the given intervals.+-- Should more than one event be scheduled to occur in any sampling interval,+-- the output list will contain all events produced during that interval.+afterEachCat :: Monad m => [(Time,b)] -> SF m a (Event [b])+afterEachCat = afterEachCat' 0+  where+    afterEachCat' :: Monad m => Time -> [(Time,b)] -> SF m a (Event [b])+    afterEachCat' _ []  = never+    afterEachCat' t qxs = MSF $ \_ -> do+      dt <- ask+      let t' = t + dt+          (qxsNow, qxsLater) = span (\p -> fst p <= t') qxs+          ev = if null qxsNow then NoEvent else Event (map snd qxsNow)+      return (ev, afterEachCat' t' qxsLater) --- | Left-biased event merge (always prefer left event, if present).-lMerge :: Event a -> Event a -> Event a-lMerge = mergeBy (\e1 _ -> e1) --- | Right-biased event merge (always prefer right event, if present).-rMerge :: Event a -> Event a -> Event a-rMerge = flip lMerge+-- * Events +instance Functor Event where+  fmap f NoEvent   = NoEvent+  fmap f (Event c) = Event (f c) +instance Applicative Event where+  pure = Event++  Event f <*> Event x = Event (f x)+  _       <*> _       = NoEvent++-- | Apply an 'MSF' to every input. Freezes temporarily if the input is+-- 'NoEvent', and continues as soon as an 'Event' is received.+mapEventS :: Monad m => MSF m a b -> MSF m (Event a) (Event b)+mapEventS msf = proc eventA -> case eventA of+  Event a -> arr Event <<< msf -< a+  NoEvent -> returnA           -< NoEvent+ -- ** Relation to other types  eventToMaybe = event Nothing Just-maybeToEvent = maybe NoEvent Event  boolToEvent :: Bool -> Event () boolToEvent True  = Event ()@@ -134,10 +206,38 @@ edge :: Monad m => SF m Bool (Event ()) edge = edgeFrom True +iEdge :: Monad m => Bool -> SF m Bool (Event ())+iEdge = edgeFrom++-- | Like 'edge', but parameterized on the tag value.+--+-- From Yampa+edgeTag :: Monad m => a -> SF m Bool (Event a)+edgeTag a = edge >>> arr (`tag` a)++-- | Edge detector particularized for detecting transtitions+--   on a 'Maybe' signal from 'Nothing' to 'Just'.+--+-- From Yampa++-- !!! 2005-07-09: To be done or eliminated+-- !!! Maybe could be kept as is, but could be easy to implement directly+-- !!! in terms of sscan?+edgeJust :: Monad m => SF m (Maybe a) (Event a)+edgeJust = edgeBy isJustEdge (Just undefined)+    where+        isJustEdge Nothing  Nothing     = Nothing+        isJustEdge Nothing  ma@(Just _) = ma+        isJustEdge (Just _) (Just _)    = Nothing+        isJustEdge (Just _) Nothing     = Nothing+ edgeBy :: Monad m => (a -> a -> Maybe b) -> a -> SF m a (Event b) edgeBy isEdge a_prev = MSF $ \a ->   return (maybeToEvent (isEdge a_prev a), edgeBy isEdge a) +maybeToEvent :: Maybe a -> Event a+maybeToEvent = maybe NoEvent Event+ edgeFrom :: Monad m => Bool -> SF m Bool (Event()) edgeFrom prev = MSF $ \a -> do   let res | prev      = NoEvent@@ -146,28 +246,13 @@       ct  = edgeFrom a   return (res, ct) +-- * Stateful event suppression+ -- | Suppression of initial (at local time 0) event. notYet :: Monad m => SF m (Event a) (Event a) notYet = feedback False $ arr (\(e,c) ->   if c then (e, True) else (NoEvent, True)) -hold :: Monad m => a -> SF m (Event a) a-hold a = feedback a $ arr $ \(e,a') ->-  dup (event a' id e)- where dup x = (x,x)--loopPre :: Monad m => c -> SF m (a, c) (b, c) -> SF m a b-loopPre = feedback---- | Event source that never occurs.-never :: Monad m => SF m a (Event b)-never = constant NoEvent---- | Event source with a single occurrence at time 0. The value of the event--- is given by the function argument.-now :: Monad m => b -> SF m a (Event b)-now b0 = Event b0 --> never- -- | Suppress all but the first event. once :: Monad m => SF m (Event a) (Event a) once = takeEvents 1@@ -177,57 +262,169 @@ takeEvents n | n <= 0 = never takeEvents n = dSwitch (arr dup) (const (NoEvent >-- takeEvents (n - 1))) -after :: Monad m-      => Time -- ^ The time /q/ after which the event should be produced-      -> b    -- ^ Value to produce at that time-      -> SF m a (Event b)-after q x = feedback q go- where go = MSF $ \(_, t) -> do-              dt <- ask-              let t' = t - dt-                  e  = if t > 0 && t' < 0 then Event x else NoEvent-                  ct = if t' < 0 then constant (NoEvent, t') else go-              return ((e, t'), ct)+-- | Suppress first n events. -occasionally :: MonadRandom m-             => Time -- ^ The time /q/ after which the event should be produced on average-             -> b    -- ^ Value to produce at time of event-             -> SF m a (Event b)-occasionally tAvg b-  | tAvg <= 0 = error "dunai: Non-positive average interval in occasionally."-  | otherwise = proc _ -> do-      r   <- getRandomRS (0, 1) -< ()-      dt  <- timeDelta          -< ()-      let p = 1 - exp (-(dt / tAvg))-      returnA -< if r < p then Event b else NoEvent- where-  timeDelta :: Monad m => SF m a DTime-  timeDelta = constM ask+-- Here dSwitch or switch does not really matter.+dropEvents :: Monad m => Int -> SF m (Event a) (Event a)+dropEvents n | n <= 0  = identity+dropEvents n = dSwitch (never &&& identity)+                             (const (NoEvent >-- dropEvents (n - 1))) --- | Initialization operator (cf. Lustre/Lucid Synchrone).+-- * Pointwise functions on events++noEvent :: Event a+noEvent = NoEvent++-- | Suppress any event in the first component of a pair.+noEventFst :: (Event a, b) -> (Event c, b)+noEventFst (_, b) = (NoEvent, b)+++-- | Suppress any event in the second component of a pair.+noEventSnd :: (a, Event b) -> (a, Event c)+noEventSnd (a, _) = (a, NoEvent)++event :: a -> (b -> a) -> Event b -> a+event _ f (Event x) = f x+event x _ NoEvent   = x++fromEvent (Event x) = x+fromEvent _         = error "fromEvent NoEvent"++isEvent (Event _) = True+isEvent _         = False++isNoEvent (Event _) = False+isNoEvent _         = True++tag :: Event a -> b -> Event b+tag NoEvent   _ = NoEvent+tag (Event _) b = Event b++-- | Tags an (occurring) event with a value ("replacing" the old value). Same+-- as 'tag' with the arguments swapped. ----- The output at time zero is the first argument, and from--- that point on it behaves like the signal function passed as--- second argument.-(-->) :: Monad m => b -> SF m a b -> SF m a b-b0 --> sf = sf >>> replaceOnce b0+-- Applicative-based definition:+-- tagWith = (<$)+tagWith :: b -> Event a -> Event b+tagWith = flip tag --- | Input initialization operator.+-- | Attaches an extra value to the value of an occurring event.+attach :: Event a -> b -> Event (a, b)+e `attach` b = fmap (\a -> (a, b)) e++-- | Left-biased event merge (always prefer left event, if present).+lMerge :: Event a -> Event a -> Event a+lMerge = mergeBy (\e1 _ -> e1)++-- | Right-biased event merge (always prefer right event, if present).+rMerge :: Event a -> Event a -> Event a+rMerge = flip lMerge++merge :: Event a -> Event a -> Event a+merge = mergeBy $ error "Bearriver: merge: Simultaneous event occurrence."++mergeBy :: (a -> a -> a) -> Event a -> Event a -> Event a+mergeBy _       NoEvent      NoEvent      = NoEvent+mergeBy _       le@(Event _) NoEvent      = le+mergeBy _       NoEvent      re@(Event _) = re+mergeBy resolve (Event l)    (Event r)    = Event (resolve l r)++-- | A generic event merge-map utility that maps event occurrences,+-- merging the results. The first three arguments are mapping functions,+-- the third of which will only be used when both events are present.+-- Therefore, 'mergeBy' = 'mapMerge' 'id' 'id' ----- The input at time zero is the first argument, and from--- that point on it behaves like the signal function passed as--- second argument.-(>--) :: Monad m => a -> SF m a b -> SF m a b-a0 >-- sf = replaceOnce a0 >>> sf+-- Applicative-based definition:+-- mapMerge lf rf lrf le re = (f <$> le <*> re) <|> (lf <$> le) <|> (rf <$> re)+mapMerge :: (a -> c) -> (b -> c) -> (a -> b -> c)+            -> Event a -> Event b -> Event c+mapMerge _  _  _   NoEvent   NoEvent   = NoEvent+mapMerge lf _  _   (Event l) NoEvent   = Event (lf l)+mapMerge _  rf _   NoEvent   (Event r) = Event (rf r)+mapMerge _  _  lrf (Event l) (Event r) = Event (lrf l r) -replaceOnce :: Monad m => a -> SF m a a-replaceOnce a = dSwitch (arr $ const (a, Event ())) (const $ arr id)+-- | Merge a list of events; foremost event has priority.+--+-- Foldable-based definition:+-- mergeEvents :: Foldable t => t (Event a) -> Event a+-- mergeEvents =  asum+mergeEvents :: [Event a] -> Event a+mergeEvents = foldr lMerge NoEvent -accumHoldBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) b-accumHoldBy f b = feedback b $ arr $ \(a, b') ->-  let b'' = event b' (f b') a-  in (b'', b'')+-- | Collect simultaneous event occurrences; no event if none.+--+-- Traverable-based definition:+-- catEvents :: Foldable t => t (Event a) -> Event (t a)+-- carEvents e  = if (null e) then NoEvent else (sequenceA e)+catEvents :: [Event a] -> Event [a]+catEvents eas = case [ a | Event a <- eas ] of+                    [] -> NoEvent+                    as -> Event as +-- | Join (conjunction) of two events. Only produces an event+-- if both events exist.+--+-- Applicative-based definition:+-- joinE = liftA2 (,)+joinE :: Event a -> Event b -> Event (a,b)+joinE NoEvent   _         = NoEvent+joinE _         NoEvent   = NoEvent+joinE (Event l) (Event r) = Event (l,r)++-- | Split event carrying pairs into two events.+splitE :: Event (a,b) -> (Event a, Event b)+splitE NoEvent       = (NoEvent, NoEvent)+splitE (Event (a,b)) = (Event a, Event b)++------------------------------------------------------------------------------+-- Event filtering+------------------------------------------------------------------------------++-- | Filter out events that don't satisfy some predicate.+filterE :: (a -> Bool) -> Event a -> Event a+filterE p e@(Event a) = if p a then e else NoEvent+filterE _ NoEvent     = NoEvent+++-- | Combined event mapping and filtering. Note: since 'Event' is a 'Functor',+-- see 'fmap' for a simpler version of this function with no filtering.+mapFilterE :: (a -> Maybe b) -> Event a -> Event b+mapFilterE _ NoEvent   = NoEvent+mapFilterE f (Event a) = case f a of+                            Nothing -> NoEvent+                            Just b  -> Event b+++-- | Enable/disable event occurences based on an external condition.+gate :: Event a -> Bool -> Event a+_ `gate` False = NoEvent+e `gate` True  = e++-- * Switching++-- ** Basic switchers++switch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b+switch sf sfC = MSF $ \a -> do+  (o, ct) <- unMSF sf a+  case o of+    (_, Event c) -> unMSF (sfC c) a+    (b, NoEvent) -> return (b, switch ct sfC)++dSwitch ::  Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b+dSwitch sf sfC = MSF $ \a -> do+  (o, ct) <- unMSF sf a+  case o of+    (b, Event c) -> do (_,ct') <- unMSF (sfC c) a+                       return (b, ct')+    (b, NoEvent) -> return (b, dSwitch ct sfC)+++-- * Parallel composition and switching++-- ** Parallel composition and switching over collections with broadcasting+ #if MIN_VERSION_base(4,8,0) parB :: (Monad m) => [SF m a b] -> SF m a [b] #else@@ -248,20 +445,7 @@              NoEvent -> dpSwitchB sfs' sfF' sfCs   return (bs, ct) -dSwitch ::  Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b-dSwitch sf sfC = MSF $ \a -> do-  (o, ct) <- unMSF sf a-  case o of-    (b, Event c) -> do (_,ct') <- unMSF (sfC c) a-                       return (b, ct')-    (b, NoEvent) -> return (b, dSwitch ct sfC)--switch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b-switch sf sfC = MSF $ \a -> do-  (o, ct) <- unMSF sf a-  case o of-    (_, Event c) -> unMSF (sfC c) a-    (b, NoEvent) -> return (b, switch ct sfC)+-- ** Parallel composition over collections  parC :: Monad m => SF m a b -> SF m [a] [b] parC sf = parC' [sf]@@ -273,6 +457,52 @@       cts = fmap snd os   return (bs, parC' cts) +-- * Discrete to continuous-time signal functions++-- ** Wave-form generation++hold :: Monad m => a -> SF m (Event a) a+hold a = feedback a $ arr $ \(e,a') ->+    dup (event a' id e)+  where+    dup x = (x,x)++-- ** Accumulators++-- | Accumulator parameterized by the accumulation function.+accumBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) (Event b)+accumBy f b = mapEventS $ accumulateWith (flip f) b++accumHoldBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) b+accumHoldBy f b = feedback b $ arr $ \(a, b') ->+  let b'' = event b' (f b') a+  in (b'', b'')++-- * State keeping combinators++-- ** Loops with guaranteed well-defined feedback+loopPre :: Monad m => c -> SF m (a, c) (b, c) -> SF m a b+loopPre = feedback++-- * Integration and differentiation++integral :: (Monad m, VectorSpace a s) => SF m a a+integral = integralFrom zeroVector++integralFrom :: (Monad m, VectorSpace a s) => a -> SF m a a+integralFrom a0 = proc a -> do+  dt <- constM ask         -< ()+  accumulateWith (^+^) a0 -< realToFrac dt *^ a++derivative :: (Monad m, VectorSpace a s) => SF m a a+derivative = derivativeFrom zeroVector++derivativeFrom :: (Monad m, VectorSpace a s) => a -> SF m a a+derivativeFrom a0 = proc a -> do+  dt   <- constM ask   -< ()+  aOld <- MSF.iPre a0 -< a+  returnA             -< (a ^-^ aOld) ^/ realToFrac dt+ -- NOTE: BUG in this function, it needs two a's but we -- can only provide one iterFrom :: Monad m => (a -> a -> DTime -> b -> b) -> b -> SF m a b@@ -281,6 +511,27 @@   let b' = f a a dt b   return (b, iterFrom f b') +-- * Noise (random signal) sources and stochastic event sources++occasionally :: MonadRandom m+             => Time -- ^ The time /q/ after which the event should be produced on average+             -> b    -- ^ Value to produce at time of event+             -> SF m a (Event b)+occasionally tAvg b+  | tAvg <= 0 = error "bearriver: Non-positive average interval in occasionally."+  | otherwise = proc _ -> do+      r   <- getRandomRS (0, 1) -< ()+      dt  <- timeDelta          -< ()+      let p = 1 - exp (-(dt / tAvg))+      returnA -< if r < p then Event b else NoEvent+ where+  timeDelta :: Monad m => SF m a DTime+  timeDelta = constM ask++-- * Execution/simulation++-- ** Reactimation+ reactimate :: Monad m => m a -> (Bool -> m (DTime, Maybe a)) -> (Bool -> b -> m Bool) -> SF Identity a b -> m () reactimate senseI sense actuate sf = do   -- runMaybeT $ MSF.reactimate $ liftMSFTrans (senseSF >>> sfIO) >>> actuateSF@@ -303,7 +554,45 @@         switch sf sfC = MSF.switch (sf >>> second (arr eventToMaybe)) sfC --- * Auxiliary+-- * Debugging / Step by step simulation++-- | Evaluate an SF, and return an output and an initialized SF.+--+--   /WARN/: Do not use this function for standard simulation. This function is+--   intended only for debugging/testing. Apart from being potentially slower+--   and consuming more memory, it also breaks the FRP abstraction by making+--   samples discrete and step based.+evalAtZero :: SF Identity a b -> a -> (b, SF Identity a b)+evalAtZero sf a = runIdentity $ runReaderT (unMSF sf a) 0++-- | Evaluate an initialized SF, and return an output and a continuation.+--+--   /WARN/: Do not use this function for standard simulation. This function is+--   intended only for debugging/testing. Apart from being potentially slower+--   and consuming more memory, it also breaks the FRP abstraction by making+--   samples discrete and step based.+evalAt :: SF Identity a b -> DTime -> a -> (b, SF Identity a b)+evalAt sf dt a = runIdentity $ runReaderT (unMSF sf a) dt++-- | Given a signal function and time delta, it moves the signal function into+--   the future, returning a new uninitialized SF and the initial output.+--+--   While the input sample refers to the present, the time delta refers to the+--   future (or to the time between the current sample and the next sample).+--+--   /WARN/: Do not use this function for standard simulation. This function is+--   intended only for debugging/testing. Apart from being potentially slower+--   and consuming more memory, it also breaks the FRP abstraction by making+--   samples discrete and step based.+--+evalFuture :: SF Identity a b -> a -> DTime -> (b, SF Identity a b)+evalFuture sf = flip (evalAt sf)++-- * Auxiliary functions++-- ** Event handling+replaceOnce :: Monad m => a -> SF m a a+replaceOnce a = dSwitch (arr $ const (a, Event ())) (const $ arr id)  -- ** Tuples dup  x     = (x,x)
src/FRP/Yampa.hs view
@@ -1,4 +1,4 @@-module FRP.Yampa (module X, SF) where+module FRP.Yampa (module X, SF, FutureSF) where  import           FRP.BearRiver         as X hiding (andThen, SF) import           FRP.Yampa.AffineSpace as X@@ -11,4 +11,5 @@ import           Data.Functor.Identity import qualified FRP.BearRiver         as BR -type SF = BR.SF Identity+type SF       = BR.SF Identity+type FutureSF = BR.SF Identity