{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, TypeFamilies #-} {-# OPTIONS_GHC -Wall #-} ---------------------------------------------------------------------- -- | -- Module : FRP.Reactive.Behavior -- Copyright : (c) Conal Elliott 2008 -- License : BSD3 -- -- Maintainer : conal@conal.net -- Stability : experimental -- -- Reactive behaviors (continuous time) ---------------------------------------------------------------------- module FRP.Reactive.Behavior ( BehaviorG, Behavior , time , stepper, switcher --, select , snapshotWith, snapshot, snapshot_ , accumB , scanlB, monoidB, maybeB, flipFlop, countB , sumB, integral ) where import Data.Monoid (Monoid(..)) import Control.Applicative (Applicative,pure,(<$>)) -- import Control.Monad (join) import Data.VectorSpace import qualified FRP.Reactive.Reactive as R import FRP.Reactive.Reactive (TimeT, ITime, Event, withTimeE, onceRestE, diffE) import FRP.Reactive.Fun import FRP.Reactive.Internal.Behavior -- | Time-specialized behaviors. -- Note: The signatures of all of the behavior functions can be generalized. Is -- the interface generality worth the complexity? type Behavior = BehaviorG ITime TimeT -- | The identity generalized behavior. Has value @t@ at time @t@. time :: Behavior TimeT time = beh (pure (fun id)) -- Turn a reactive value into a discretly changing behavior. rToB :: R.Reactive a -> Behavior a rToB = beh . fmap pure -- Then use 'rToB' to promote reactive value functions to behavior -- functions. -- | Discretely changing behavior, based on an initial value and a -- new-value event. stepper :: a -> Event a -> Behavior a stepper = (fmap.fmap) rToB R.stepper -- Suggested by Robin Green: -- stepper = select pure -- -- | Use a key event to key into a behaviour-valued function -- select :: (a -> Behavior b) -> a -> Event a -> Behavior b -- select f a e = f a `switcher` (f <$> e) -- Looking for a more descriptive name. -- | Switch between behaviors. switcher :: Behavior a -> Event (Behavior a) -> Behavior a b `switcher` eb = beh (unb b `R.switcher` (unb <$> eb)) -- | Snapshots a behavior whenever an event occurs and combines the values -- using the combining function passed. snapshotWith :: (a -> b -> c) -> Event a -> Behavior b -> Event c snapshotWith h e b = f <$> (withTimeE e `R.snapshot` unb b) where f ((a,t),tfun) = h a (tfun `apply` t) -- | Snapshot a behavior whenever an event occurs. See also 'snapshotWith'. snapshot :: Event a -> Behavior b -> Event (a,b) snapshot = snapshotWith (,) -- Alternative implementations: -- snapshotWith c e b = uncurry c <$> snapshot e b -- snapshotWith c = (fmap.fmap.fmap) (uncurry c) snapshot -- | Like 'snapshot' but discarding event data (often @a@ is '()'). snapshot_ :: Event a -> Behavior b -> Event b snapshot_ = snapshotWith (flip const) -- Alternative implementations -- e `snapshot_` src = snd <$> (e `snapshot` src) -- snapshot_ = (fmap.fmap.fmap) snd snapshot -- | Behavior from an initial value and an updater event. See also -- 'accumE'. accumB :: a -> Event (a -> a) -> Behavior a accumB = (fmap.fmap) rToB R.accumR -- -- | Like 'scanl' for behaviors. See also 'scanlE'. -- scanlB :: (a -> b -> a) -> a -> Event b -> Behavior a -- scanlB = (fmap.fmap.fmap) rToB R.scanlR -- -- | Accumulate values from a monoid-valued event. Specialization of -- -- 'scanlB', using 'mappend' and 'mempty'. See also 'monoidE'. -- monoidB :: Monoid a => Event a -> Behavior a -- monoidB = fmap rToB R.monoidR ---- The next versions are more continuous: -- type RF a = R.Reactive (Fun TimeT a) -- scanlB :: forall a c. (Behavior a -> c -> Behavior a) -> Behavior a -- -> Event c -> Behavior a -- scanlB f b0 e = beh (scanlRF f' (unb b0) e) -- where -- f' :: RF a -> c -> RF a -- f' r c = unb (f (beh r) c) -- scanlRF :: (RF a -> c -> RF a) -> RF a -> Event c -> RF a -- scanlRF h rf0 e = join (R.scanlR h rf0 e) -- monoidB :: Monoid a => Event (Behavior a) -> Behavior a -- monoidB = scanlB mappend mempty -- -- I doubt these definitions work well. They accumulate reactives without -- -- aging them. See 'accumE'. -- | Like 'scanl' for behaviors. See also 'scanlE'. scanlB :: forall a. (Behavior a -> Behavior a -> Behavior a) -> Behavior a -> Event (Behavior a) -> Behavior a scanlB plus zero = h where h :: Event (Behavior a) -> Behavior a h e = zero `switcher` (g <$> onceRestE e) g :: (Behavior a, Event (Behavior a)) -> Behavior a g (b, e') = b `plus` h e' -- | Accumulate values from a monoid-valued event. Specialization of -- 'scanlB', using 'mappend' and 'mempty'. See also 'monoidE'. monoidB :: Monoid a => Event (Behavior a) -> Behavior a monoidB = scanlB mappend mempty -- | Like 'sum' for behaviors. sumB :: VectorSpace v => Event v -> Behavior v sumB = fmap rToB R.sumR -- | Start out blank ('Nothing'), latching onto each new @a@, and blanking -- on each @b@. If you just want to latch and not blank, then use -- 'mempty' for the second event. maybeB :: Event a -> Event b -> Behavior (Maybe a) maybeB = (fmap.fmap) rToB R.maybeR -- | Flip-flopping behavior. Turns true whenever first event occurs and -- false whenever the second event occurs. flipFlop :: Event a -> Event b -> Behavior Bool flipFlop = (fmap.fmap) rToB R.flipFlop -- | Count occurrences of an event. See also 'countE'. countB :: Num n => Event a -> Behavior n countB = fmap rToB R.countR -- | Euler integral. integral :: (VectorSpace v, Scalar v ~ TimeT) => Event () -> Behavior v -> Behavior v integral t = sumB . snapshotWith (*^) (diffE (t `snapshot_` time)) -- TODO: find out whether this integral works recursively. If not, then -- fix the implementation, rather than changing the semantics. (No -- "delayed integral".) -- -- Early experiments suggest that recursive integration gets stuck. -- Investigate.