reactive 0.8.6 → 0.8.8
raw patch · 9 files changed
+157/−149 lines, 9 filesdep ~vector-space
Dependency ranges changed: vector-space
Files
- announce +9/−0
- reactive.cabal +2/−2
- src/FRP/Reactive.hs +2/−2
- src/FRP/Reactive/Behavior.hs +48/−10
- src/FRP/Reactive/Internal/Behavior.hs +12/−4
- src/FRP/Reactive/LegacyAdapters.hs +2/−2
- src/FRP/Reactive/PrimReactive.hs +9/−121
- src/FRP/Reactive/Reactive.hs +68/−6
- src/FRP/Reactive/VectorSpace.hs +5/−2
+ announce view
@@ -0,0 +1,9 @@+Reactive [1] is a library for functional reactive programming (FRP), similar to the original Fran [2] but with a more modern interface (using standard type classes) and a hybrid push/pull implementation. It is designed to be used in a variety of contexts, such as interactive 2D and 3D graphics, graphical user interfaces, web services, and automatic recompilation/re-execution. It has a simple and precise semantics based on continuous time and is built on a notion of functional future values. The semantics and implementation are described in the paper "Simply efficient functional reactivity" [3].++Reactive now has a mailing list [4] and a feature/bug tracker [5].++[1] http://haskell.org/haskellwiki/Reactive+[2] http://conal.net/Fran+[3] http://conal.net/papers/simply-reactive+[4] http://www.haskell.org/mailman/listinfo/reactive+[5] http://trac.haskell.org/reactive
reactive.cabal view
@@ -1,5 +1,5 @@ Name: reactive-Version: 0.8.6+Version: 0.8.8 Synopsis: Simple foundation for functional reactive programming Category: reactivity, FRP Description:@@ -33,7 +33,7 @@ Extra-Source-Files: Library Build-Depends: base, old-time, random, QuickCheck < 2.0,- TypeCompose>=0.3, vector-space, unamb, checkers+ TypeCompose>=0.3, vector-space>=0.5, unamb, checkers -- This library uses the ImpredicativeTypes flag, and it depends -- on vector-space, which needs ghc >= 6.9 if impl(ghc < 6.9) {
src/FRP/Reactive.hs view
@@ -17,14 +17,14 @@ TimeT, ITime , EventG, Event , accumE- , withTimeE+ , withTimeE, withTimeE_ , pairE, scanlE, monoidE , stateE, stateE_, countE, countE_, diffE , withPrevE, withPrevEWith , whenE, eitherE -- ** More esoteric , listE, atTimes, atTime, once- , firstRestE, firstE, restE+ , firstRestE, firstE, restE, snapRemainderE , withRestE, untilE , splitE, switchE -- ** Useful with events.
src/FRP/Reactive/Behavior.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, TypeFamilies #-} {-# OPTIONS_GHC -Wall #-} ---------------------------------------------------------------------- -- |@@ -23,13 +23,14 @@ , sumB, integral ) where -import Data.Monoid (Monoid)+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, diffE)+import FRP.Reactive.Reactive (TimeT, ITime, Event, withTimeE, onceRestE, diffE) import FRP.Reactive.Fun import FRP.Reactive.Internal.Behavior @@ -97,17 +98,54 @@ 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 :: (a -> b -> a) -> a -> Event b -> Behavior a-scanlB = (fmap.fmap.fmap) rToB R.scanlR+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--- 'scanlE', using 'mappend' and 'mempty'. See also 'monoidE'.-monoidB :: Monoid a => Event a -> Behavior a-monoidB = fmap rToB R.monoidR+-- '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 s => Event v -> Behavior v+sumB :: VectorSpace v => Event v -> Behavior v sumB = fmap rToB R.sumR -- | Start out blank ('Nothing'), latching onto each new @a@, and blanking@@ -126,7 +164,7 @@ countB = fmap rToB R.countR -- | Euler integral.-integral :: VectorSpace v TimeT =>+integral :: (VectorSpace v, Scalar v ~ TimeT) => Event () -> Behavior v -> Behavior v integral t = sumB . snapshotWith (*^) (diffE (t `snapshot_` time))
src/FRP/Reactive/Internal/Behavior.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE TypeOperators, GeneralizedNewtypeDeriving #-}-{-# OPTIONS_GHC -Wall #-}+{-# LANGUAGE TypeOperators, GeneralizedNewtypeDeriving+ , FlexibleInstances, FlexibleContexts #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-} ---------------------------------------------------------------------- -- | -- Module : FRP.Reactive.Internal.Behavior@@ -15,7 +16,8 @@ module FRP.Reactive.Internal.Behavior (BehaviorG(..), beh, unb) where -import Control.Applicative (Applicative)+import Data.Monoid (Monoid(..))+import Control.Applicative (Applicative(pure),liftA2) -- TypeCompose import Control.Compose ((:.)(..))@@ -53,7 +55,13 @@ -- == liftA2 mappend@. That is, @mempty `at` t == mempty@, and @(r -- `mappend` s) `at` t == (r `at` t) `mappend` (s `at` t).@ newtype BehaviorG tr tf a = Beh { unBeh :: (R.ReactiveG tr :. Fun tf) a }- deriving (Functor,Applicative)+ deriving (Monoid,Functor,Applicative)++-- Standard Monoid instance for Applicative applied to Monoid. Used by+-- @deriving Monoid@ above.+instance (Applicative (R.ReactiveG tr :. Fun tf), Monoid a)+ => Monoid ((R.ReactiveG tr :. Fun tf) a) where+ { mempty = pure mempty; mappend = liftA2 mappend } -- | Wrap a reactive time fun as a behavior. beh :: R.ReactiveG tr (Fun tf a) -> BehaviorG tr tf a
src/FRP/Reactive/LegacyAdapters.hs view
@@ -14,12 +14,12 @@ module FRP.Reactive.LegacyAdapters ( Sink, Action- , makeClock, cGetTime+ , Clock, makeClock, cGetTime , makeEvent , mkUpdater ) where import FRP.Reactive.Internal.Misc (Sink,Action)-import FRP.Reactive.Internal.Clock (makeClock,cGetTime)+import FRP.Reactive.Internal.Clock (Clock,makeClock,cGetTime) import FRP.Reactive.Internal.TVal (makeEvent) import FRP.Reactive.Internal.Timing (mkUpdater)
src/FRP/Reactive/PrimReactive.hs view
@@ -45,8 +45,7 @@ , stepper, switcher, withTimeGE, withTimeGR , futuresE, listEG, atTimesG, atTimeG , snapshotWith, accumE, accumR, once- , firstRestE, firstE, restE- , remainderR, withRestE, untilE+ , withRestE, untilE -- , traceE, traceR -- , mkEvent, mkEventTrace, mkEventShow , eventOcc@@ -114,9 +113,7 @@ instance (Arbitrary t, Ord t, Num t, Arbitrary a) => Arbitrary (EventG t a) where arbitrary = arbitraryE- -- TODO: Fix this coarbitrary instance -- David- coarbitrary = error "coarbitrary Events not supported"- -- coarbitrary = coarbitrary . eFuture+ coarbitrary = coarbitrary . eFuture ---- @@ -346,7 +343,7 @@ -- | Single-occurrence event at given time. atTimeG :: Ord t => t -> EventG t ()-atTimeG t = futuresE (pure (future t ()))+atTimeG = atTimesG . pure -- This variant of 'snapshot' has 'Nothing's where @b@ changed and @a@ -- didn't.@@ -370,12 +367,6 @@ -- | Accumulating event, starting from an initial value and a -- update-function event. See also 'accumR'.--- Example: (using a list rempresentation for events, for clarity--- @10 `accumE`--- [(5 seconds, (+2)),(10 seconds, (subtract 30)),(20 seconds,(*10))]--- = [(5 seconds, 12),(10 seconds, -18),(20 seconds, -180)]@--- If you want an initial occurance at @-infinity@ you can use @pure a--- `mappend` accumE a e@ accumE :: a -> EventG t (a -> a) -> EventG t a accumE a = inEvent $ fmap $ \ (f `Stepper` e') -> f a `accumR` e' @@ -388,43 +379,12 @@ once :: Ord t => EventG t a -> EventG t a once = inEvent $ fmap $ pure . rInit --- | Decompose an event into its first occurrence value and a remainder--- event. See also 'firstE' and 'restE'.-firstRestE :: Ord t => EventG t a -> (a, EventG t a)-firstRestE (Event fut) = f (futVal fut)- where- f (a `Stepper` b) = (a,b)---- | Extract the first occurrence value of an event. See also--- 'firstRestE' and 'restE'.-firstE :: Ord t => EventG t a -> a-firstE = fst . firstRestE---- | Extract the remainder an event, after its first occurrence. See also--- 'firstRestE' and 'firstE'.-restE :: Ord t => EventG t a -> EventG t a-restE = snd . firstRestE+-- | Extract a future representing the first occurrence of the event together+-- with the event of all occurrences after that one.+eventOcc :: (Ord t) => EventG t a -> FutureG t (a, EventG t a)+eventOcc (Event fut) = (\ (Stepper a e) -> (a,e)) <$> fut --- | Remaining part of an event. See also 'withRestE'.-remainderR :: Ord t => EventG t a -> ReactiveG t (EventG t a)-remainderR e = e `stepper` (snd <$> withRestE e)---- -- | Event remainders. Replace event values with a reactive that starts--- -- with that value and follows the event. Sort of like 'tails'.--- eventR :: Ord t => EventG t a -> EventG t (ReactiveG t a)--- eventR = inEvent $ fmap $ \ r@(_ `Stepper` e') -> r `Stepper` eventR e'---- Also try the following definition of remainderR---- remainderR :: forall t a. Ord t => EventG t a -> ReactiveG t (EventG t a)--- remainderR e = e `accumR` (next <$ e)--- where--- next :: Unop (EventG t a)--- next ~(Event (Future (_, _ `Stepper` e'))) = e'---- newtype EventG t a = Event { eFuture :: FutureG t (ReactiveG t a) }- -- | Access the remainder with each event occurrence. withRestE :: EventG t a -> EventG t (a, EventG t a) withRestE = inEvent $ fmap $@@ -459,80 +419,7 @@ -- I'm not sure about @<@ vs @<=@ above. -{---- | Tracing of events.-traceE :: Show t => (a -> String) -> EventG t a -> EventG t a --- traceE shw = fmap (\ (t,a) -> trace (shw' t a) a) . withTimeGE--- where--- shw' t a = "time "++show t++": "++shw a---- traceE shw = fmap (\ (t,a) -> trace (shw' t) a) . withTimeGE--- where--- shw' t = "time "++show t++"\n"---- traceE shw = fmap (\ a -> trace (shw a) a)---- Something is wonky. Try this version, avoiding withTimeGE--traceE shw ~(Event (Future (t,r))) =- Event (Future (trace ("time "++show t) t, traceR shw r))---- | Tracing of reactive values-traceR :: Show t => (a -> String) -> Unop (ReactiveG t a)-traceR shw ~(a `Stepper` e) = trace ("val: "++shw a) $- a `Stepper` traceE shw e--}---- I'm experimenting with lazy patterns here. They didn't help.--- When time tracing is on, mappends don't work. I think the problem is--- that show extracts *all* information from a time, while 'min' and--- '(<=)' don't. Of course: consider two future occurrences being--- compared. Before any outer info can be extracted, the trace will--- evaluate the whole time of a occurrence that hasn't happened yet.--- --- To trace an event then, I really want to put partial traces into the--- times, which will have to work specially for the time type. Or I could--- make a Traceable class.--{----- | Make an event and a sink for feeding the event. Each value sent to--- the sink becomes an occurrence of the event.-mkEvent :: Ord t => IO (EventG t a, SinkG t a)-mkEvent = do (fut,handler) <- newFuture- -- remember how to save the next occurrence.- r <- newIORef handler- return (Event fut, writeTo r)- where- -- Fill in an occurrence while preparing for the next one- writeTo r fut = do handler <- readIORef r- (fut',handler') <- newFuture- writeIORef r handler'- handler $ fmap (`stepper` Event fut') fut---- TODO: replace IORefs by mvars. When I tried before, GuiTV input hung.---- | Tracing variant of 'mkEvent'-mkEventTrace :: (Ord t, Show t) =>- (a -> String) -> IO (EventG t a, SinkG t a)-mkEventTrace shw = second tr <$> mkEvent- where- tr handler = (putStrLn.shw') `mappend` handler- shw' (Future (t,a)) = "Occurrence at time "++show t++": "++shw a---- | Show specialization of 'mkEventTrace'-mkEventShow :: (Ord t, Show t, Show a) => String -> IO (EventG t a, SinkG t a)-mkEventShow str = mkEventTrace ((str ++).(' ':).show)---}---- | Get a future representing the first occurrence of the event together--- with the event of all occurrences after that one.-eventOcc :: (Ord t) => EventG t a -> FutureG t (a, EventG t a)-eventOcc (Event fut) = (\ (Stepper a e) -> (a,e)) <$> fut-- -- | Sample a reactive value at a sequence of monotonically non-decreasing -- times. Deprecated, because it does not reveal when value is known to -- be repeated in the output. Those values won't be recomputed, but they@@ -754,6 +641,7 @@ isStillOrderedR' a (b `Stepper` e) = a < b && isStillOrderedE' b e --- An event to test with that is infinite+-- An infinite event. handy for testing. infE :: EventG NumT NumT infE = futuresE (zipWith future [1..] [1..]) +
src/FRP/Reactive/Reactive.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE TypeSynonymInstances, ScopedTypeVariables, TypeOperators, FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances, ScopedTypeVariables, TypeOperators+ , FlexibleInstances, TypeFamilies+ #-} {-# OPTIONS_GHC -Wall #-} ---------------------------------------------------------------------- -- |@@ -20,10 +22,12 @@ , traceF -- * Event , Event- , withTimeE+ , withTimeE, withTimeE_ , atTime, atTimes, listE , {-mbsEvent,-} pairE, scanlE, monoidE- , withPrevE, withPrevEWith+ , firstRestE, firstE, restE+ , remainderR, snapRemainderE, onceRestE+ , withPrevE, withPrevEWith, withNextE, withNextEWith , stateE, stateE_, countE, countE_, diffE -- * Reactive values , Reactive@@ -94,6 +98,11 @@ withTimeE :: Event a -> Event (a, TimeT) withTimeE e = second (exact.timeT) <$> withTimeGE e +-- | Access occurrence times in an event. Discard the rest. See also+-- 'withTimeE'.+withTimeE_ :: Event a -> Event TimeT+withTimeE_ = (fmap.fmap) snd withTimeE+ timeT :: Ord t => Time t -> t timeT (Max (NoBound t)) = t timeT _ = error "timeT: non-finite time"@@ -125,6 +134,45 @@ monoidE :: (Ord t, Monoid o) => EventG t o -> EventG t o monoidE = scanlE mappend mempty +++-- | Decompose an event into its first occurrence value and a remainder+-- event. See also 'firstE' and 'restE'.+firstRestE :: Ord t => EventG t a -> (a, EventG t a)+firstRestE = futVal . eventOcc++-- | Extract the first occurrence value of an event. See also+-- 'firstRestE' and 'restE'.+firstE :: Ord t => EventG t a -> a+firstE = fst . firstRestE++-- | Extract the remainder an event, after its first occurrence. See also+-- 'firstRestE' and 'firstE'.+restE :: Ord t => EventG t a -> EventG t a+restE = snd . firstRestE++++-- | Remaining part of an event. See also 'withRestE'.+remainderR :: Ord t => EventG t a -> ReactiveG t (EventG t a)+remainderR e = e `stepper` (snd <$> withRestE e)+++-- | Tack remainders a second event onto values of a first event. Occurs+-- when the first event occurs.+snapRemainderE :: Event a -> Event b -> Event (a, Event b)+snapRemainderE ea eb = ea `snapshot` remainderR eb++-- withTailE ea eb = error "withTailE: undefined" ea eb+++-- | Convert an event into a single-occurrence event, whose occurrence+-- contains the remainder.+onceRestE :: Ord t => EventG t a -> EventG t (a, EventG t a)+onceRestE = once . withRestE+++ -- | Pair each event value with the previous one. The second result is -- the old one. Nothing will come out for the first occurrence of @e@, -- but if you have an initial value @a@, you can do @withPrevE (pure a@@ -139,12 +187,26 @@ combineMaybes :: (Maybe u, Maybe v) -> Maybe (u,v) combineMaybes = uncurry (liftA2 (,)) + -- | Same as 'withPrevE', but allow a function to combine the values. -- Provided for convenience. withPrevEWith :: Ord t => (a -> a -> b) -> EventG t a -> EventG t b withPrevEWith f e = fmap (uncurry f) (withPrevE e) +-- | Pair each event value with the next one one. The second result is+-- the next one.+withNextE :: Ord t => EventG t a -> EventG t (a,a)+withNextE = (fmap.fmap.fmap) firstE withRestE+-- Alt. def.+-- withNextE = fmap (second firstE) . withRestE++-- | Same as 'withNextE', but allow a function to combine the values.+-- Provided for convenience.+withNextEWith :: Ord t => (a -> a -> b) -> EventG t a -> EventG t b+withNextEWith f e = fmap (uncurry f) (withNextE e)++ -- | State machine, given initial value and transition function. Carries -- along event data. See also 'stateE_'. TODO: better name. stateE :: Ord t => s -> (s -> s) -> EventG t b -> EventG t (b,s)@@ -259,12 +321,12 @@ -- | Switch from one event to another, as they occur. (Doesn't merge, as -- 'join' does.)-switchE :: Event (Event a) -> Event a+switchE :: Ord t => EventG t (EventG t a) -> EventG t a switchE = join . fmap (uncurry untilE) . withRestE -- | Euler integral.-integral :: forall v t. (Num t, VectorSpace v t) =>+integral :: forall v t. (VectorSpace v, t ~ Scalar v, Num t) => t -> Event t -> Reactive v -> Reactive v integral t0 newT r = sumR (snapshotWith (*^) deltaT r) where@@ -275,7 +337,7 @@ -- fix the implementation, rather than changing the semantics. (No -- "delayed integral".) -sumR :: VectorSpace v s => Event v -> Reactive v+sumR :: AdditiveGroup v => Event v -> Reactive v sumR = scanlR (^+^) zeroV
src/FRP/Reactive/VectorSpace.hs view
@@ -1,4 +1,6 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances+ , TypeFamilies+ #-} {-# OPTIONS_GHC -Wall -fno-warn-orphans #-} @@ -15,5 +17,6 @@ (^+^) = liftA2 (^+^) negateV = liftA negateV -instance VectorSpace v s => VectorSpace (Behavior v) s where+instance VectorSpace v => VectorSpace (Behavior v) where+ type Scalar (Behavior v) = Scalar v (*^) s = fmap (s *^)