reactive 0.9.4 → 0.9.5
raw patch · 6 files changed
+239/−31 lines, 6 files
Files
- reactive.cabal +1/−1
- src/FRP/Reactive/Behavior.hs +114/−27
- src/FRP/Reactive/Fun.hs +3/−0
- src/FRP/Reactive/Num-inc.hs +107/−0
- src/FRP/Reactive/PrimReactive.hs +5/−0
- src/FRP/Reactive/Reactive.hs +9/−3
reactive.cabal view
@@ -1,5 +1,5 @@ Name: reactive-Version: 0.9.4+Version: 0.9.5 Synopsis: Simple foundation for functional reactive programming Category: reactivity, FRP Description:
src/FRP/Reactive/Behavior.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, TypeFamilies- , TypeOperators+{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, TypeFamilies, TypeOperators+ , StandaloneDeriving, GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -Wall -fno-warn-orphans #-} ----------------------------------------------------------------------@@ -35,25 +35,34 @@ import Data.VectorSpace import qualified FRP.Reactive.Reactive as R-import FRP.Reactive.Reactive (TimeT, ITime, Event, withTimeE, onceRestE, diffE,joinMaybes,result)+import FRP.Reactive.Reactive+ ( TimeT, EventG, ReactiveG+ , withTimeE,onceRestE,diffE,joinMaybes,result) import FRP.Reactive.Fun+import FRP.Reactive.Improving import FRP.Reactive.Internal.Behavior +type EventI t = EventG (Improving t)+type ReactiveI t = ReactiveG (Improving t)+type BehaviorI t = BehaviorG (Improving t) t -- | 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+type Behavior = BehaviorI TimeT -- Synonym for 'Behavior' type Behaviour = Behavior + -- | The identity generalized behavior. Has value @t@ at time @t@.-time :: Behavior TimeT-time = beh (pure (fun id))+-- +-- > time :: Behavior TimeT+time :: Ord t => BehaviorI t t+time = beh (point (fun id)) -- Turn a reactive value into a discretly changing behavior.-rToB :: R.Reactive a -> Behavior a+rToB :: ReactiveI t a -> BehaviorI t a rToB = beh . fmap pure -- Then use 'rToB' to promote reactive value functions to behavior@@ -61,7 +70,9 @@ -- | Discretely changing behavior, based on an initial value and a -- new-value event.-stepper :: a -> Event a -> Behavior a+-- +-- >stepper :: a -> Event a -> Behavior a+stepper :: a -> EventI t a -> BehaviorI t a stepper = (result.result) rToB R.stepper -- Suggested by Robin Green:@@ -75,26 +86,53 @@ -- Looking for a more descriptive name. -- | Switch between behaviors.-switcher :: Behavior a -> Event (Behavior a) -> Behavior a+-- +-- > switcher :: Behavior a -> Event (Behavior a) -> Behavior a+switcher :: (Ord tr) =>+ BehaviorG tr tf a+ -> EventG tr (BehaviorG tr tf a)+ -> BehaviorG tr tf 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 :: (a -> b -> c) -> Event a -> Behavior b -> Event c+-- snapshotWith :: Ord t =>+-- (a -> b -> c) -> EventG t a -> ReactiveG t b -> EventG t c+snapshotWith :: Ord t =>+ (a -> b -> c)+ -> EventI t a -> BehaviorI t b -> EventI t c snapshotWith h e b = f <$> (withTimeE e `R.snapshot` unb b) where f ((a,t),tfun) = h a (tfun `apply` t) ++-- 'snapshotWith' is where tr meets tf. withTimeE is specialized from+-- withTimeGE, converting the ITime into a TimeT. This specialization+-- interferes with the generality of several functions in this module,+-- which are therefore now still using 'Behavior' instead of 'BehaviorG'.+-- Figure out how to get generality.++ -- | Snapshot a behavior whenever an event occurs. See also 'snapshotWith'.-snapshot :: Event a -> Behavior b -> Event (a,b)+-- +-- > snapshot :: Event a -> Behavior b -> Event (a,b)+snapshot :: Ord t => EventI t a -> BehaviorI t b -> EventI t (a,b) snapshot = snapshotWith (,) +-- TODO: tweak withTimeE so that 'snapshotWith' and 'snapshot' can have+-- more general types. The problem is that withTimeE gives a friendlier+-- kind of time, namely known and finite. Necessary?+ -- Alternative implementations: -- snapshotWith c e b = uncurry c <$> snapshot e b -- snapshotWith c = (result.result.fmap) (uncurry c) snapshot -- | Like 'snapshot' but discarding event data (often @a@ is '()').-snapshot_ :: Event a -> Behavior b -> Event b+-- +-- > snapshot_ :: Event a -> Behavior b -> Event b+snapshot_ :: Ord t => EventI t a -> BehaviorI t b -> EventI t b snapshot_ = snapshotWith (flip const) -- Alternative implementations@@ -102,15 +140,21 @@ -- snapshot_ = (result.result.fmap) snd snapshot -- | Filter an event according to whether a reactive boolean is true.-whenE :: Event a -> Behavior Bool -> Event a+-- +-- > whenE :: Event a -> Behavior Bool -> Event a+whenE :: Ord t => EventI t a -> BehaviorI t Bool -> EventI t a whenE e = joinMaybes . fmap h . snapshot e where h (a,True) = Just a h (_,False) = Nothing +-- TODO: Same comment about generality as with snapshot+ -- | Behavior from an initial value and an updater event. See also -- 'accumE'.-accumB :: a -> Event (a -> a) -> Behavior a+-- +-- > accumB :: a -> Event (a -> a) -> Behavior a+accumB :: a -> EventI t (a -> a) -> BehaviorI t a accumB = (result.result) rToB R.accumR -- -- | Like 'scanl' for behaviors. See also 'scanlE'.@@ -125,7 +169,7 @@ ---- The next versions are more continuous: --- type RF a = R.Reactive (Fun TimeT a)+-- type RF a = Reactive (Fun TimeT a) -- scanlB :: forall a c. (Behavior a -> c -> Behavior a) -> Behavior a -- -> Event c -> Behavior a@@ -145,50 +189,79 @@ -- | Like 'scanl' for behaviors. See also 'scanlE'.-scanlB :: forall a. (Behavior a -> Behavior a -> Behavior a) -> Behavior a- -> Event (Behavior a) -> Behavior a+-- +-- > scanlB :: forall a. (Behavior a -> Behavior a -> Behavior a) -> Behavior a+-- > -> Event (Behavior a) -> Behavior a++-- TODO: generalize scanlB's type++scanlB :: forall a b tr tf. Ord tr =>+ (b -> BehaviorG tr tf a -> BehaviorG tr tf a)+ -> BehaviorG tr tf a+ -> EventG tr b -> BehaviorG tr tf a scanlB plus zero = h where- h :: Event (Behavior a) -> Behavior a+ h :: EventG tr b -> BehaviorG tr tf a h e = zero `switcher` (g <$> onceRestE e)- g :: (Behavior a, Event (Behavior a)) -> Behavior a+ g :: (b, EventG tr b) -> BehaviorG tr tf 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 :: Monoid a => Event (Behavior a) -> Behavior a+monoidB :: (Ord tr, Monoid a) => EventG tr (BehaviorG tr tf a)+ -> BehaviorG tr tf a monoidB = scanlB mappend mempty -- | Like 'sum' for behaviors.-sumB :: AdditiveGroup a => Event a -> Behavior a+-- +-- > sumB :: AdditiveGroup a => Event a -> Behavior a+sumB :: (Ord t, AdditiveGroup a) => EventI t a -> BehaviorI t a sumB = result 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 :: Event a -> Event b -> Behavior (Maybe a)+maybeB :: Ord t =>+ EventI t a -> EventI t b -> BehaviorI t (Maybe a) maybeB = (result.result) 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 :: Event a -> Event b -> Behavior Bool+flipFlop :: Ord t => EventI t a -> EventI t b -> BehaviorI t Bool flipFlop = (result.result) rToB R.flipFlop -- | Count occurrences of an event. See also 'countE'.-countB :: Num n => Event a -> Behavior n+-- +-- > countB :: Num n => Event a -> Behavior n+countB :: (Ord t, Num n) => EventI t a -> BehaviorI t n countB = result rToB R.countR -- | Euler integral.-integral :: (VectorSpace v, Scalar v ~ TimeT) =>- Event () -> Behavior v -> Behavior v+-- > integral :: (VectorSpace v, Scalar v ~ TimeT) =>+-- > Event () -> Behavior v -> Behavior v+-- integral :: (VectorSpace v, Scalar v ~ TimeT) =>+-- EventI t () -> BehaviorI t v -> BehaviorI t v+integral :: (Scalar v ~ t, Ord t, VectorSpace v, Num t) =>+ EventI t a -> BehaviorI t v -> BehaviorI t v integral t = sumB . snapshotWith (*^) (diffE (t `snapshot_` time)) +-- Yow! That's a mouth full!++ -- 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.+-- Chuan-kai Lin has come up with a new lazier R.snapshotWith, but it+-- leaks when the reactive value changes in between event occurrences. ---- Comonadic stuff@@ -213,6 +286,20 @@ -- instance Comonad (g :. f) where -- duplicate ++deriving instance (Monoid tr, Monoid tf) => Copointed (BehaviorG tr tf) ++-- ITime and TimeT are not currently monoids. They can be when I wrap+-- them in the Sum monoid constructor, in which mempty = 0 and mappend =+-- (+). This monoid change moves us from absolute to relative time. What+-- do I do for never-occuring futures and terminating events?+++-- instance (Monoid tr, Monoid tf) => Comonad (BehaviorI tf) where+-- duplicate b = b `stepper` undefined+++-- TODO: generalize to BehaviorG
src/FRP/Reactive/Fun.hs view
@@ -105,6 +105,9 @@ K a' *** K b' = K (a',b') f *** g = first f >>> second g +instance Pointed (Fun t) where+ point = K+ instance Monoid t => Copointed (Fun t) where extract = extract . apply
+ src/FRP/Reactive/Num-inc.hs view
@@ -0,0 +1,107 @@+----------------------------------------------------------------------+-- Meta-Module : Num-inc+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Instances of Num classes for applicative functors. To be #include'd+-- after defining APPLICATIVE as the applicative functor name.+-- +-- You'll also have to import 'pure' and 'liftA2' from+-- "Control.Applicative".+----------------------------------------------------------------------++noOv :: String -> String -> a+noOv ty meth = error $ meth ++ ": No overloading for " ++ ty++noFun :: String -> a+noFun = noOv "behavior"++-- Eq & Show are prerequisites for Num, so they need to be faked here+instance Eq (APPLICATIVE b) where+ (==) = noFun "(==)"+ (/=) = noFun "(/=)"++instance Ord b => Ord (APPLICATIVE b) where+ min = liftA2 min+ max = liftA2 max++instance Enum a => Enum (APPLICATIVE a) where+ succ = fmap succ+ pred = fmap pred+ toEnum = pure . toEnum+ fromEnum = noFun "fromEnum"+ enumFrom = noFun "enumFrom"+ enumFromThen = noFun "enumFromThen"+ enumFromTo = noFun "enumFromTo"+ enumFromThenTo = noFun "enumFromThenTo"++instance Show (APPLICATIVE b) where+ show = noFun "show"+ showsPrec = noFun "showsPrec"+ showList = noFun "showList"++instance Num b => Num (APPLICATIVE b) where+ negate = fmap negate+ (+) = liftA2 (+)+ (*) = liftA2 (*)+ fromInteger = pure . fromInteger+ abs = fmap abs+ signum = fmap signum++instance (Num a, Ord a) => Real (APPLICATIVE a) where+ toRational = noFun "toRational"++instance Integral a => Integral (APPLICATIVE a) where+ quot = liftA2 quot+ rem = liftA2 rem+ div = liftA2 div+ mod = liftA2 mod+ quotRem = (fmap.fmap) unzip (liftA2 quotRem)+ divMod = (fmap.fmap) unzip (liftA2 divMod)+ toInteger = noFun "toInteger"++instance Fractional b => Fractional (APPLICATIVE b) where+ recip = fmap recip+ fromRational = pure . fromRational++instance Floating b => Floating (APPLICATIVE b) where+ pi = pure pi+ sqrt = fmap sqrt+ exp = fmap exp+ log = fmap log+ sin = fmap sin+ cos = fmap cos+ asin = fmap asin+ atan = fmap atan+ acos = fmap acos+ sinh = fmap sinh+ cosh = fmap cosh+ asinh = fmap asinh+ atanh = fmap atanh+ acosh = fmap acosh++instance RealFrac a => RealFrac (APPLICATIVE a) where+ properFraction = noFun "properFraction"+ truncate = noFun "truncate"+ round = noFun "round"+ ceiling = noFun "ceiling"+ floor = noFun "floor"++instance RealFloat a => RealFloat (APPLICATIVE a) where+ floatRadix = noFun "floatRadix"+ floatDigits = noFun "floatDigits"+ floatRange = noFun "floatRange"+ decodeFloat = noFun "decodeFloat"+ encodeFloat = (fmap.fmap) pure encodeFloat+ exponent = noFun "exponent"+ significand = noFun "significand"+ scaleFloat n = fmap (scaleFloat n)+ isNaN = noFun "isNaN"+ isInfinite = noFun "isInfinite"+ isDenormalized = noFun "isDenormalized"+ isNegativeZero = noFun "isNegativeZero"+ isIEEE = noFun "isIEEE"+ atan2 = liftA2 atan2
src/FRP/Reactive/PrimReactive.hs view
@@ -579,6 +579,11 @@ -- TODO: Reconsider E = F :. R . Didn't work with absolute time. What -- about relative time? +instance Ord t => Pointed (ReactiveG t) where+ point = (`stepper` mempty)++-- TODO: I think we can bypass mempty and so eliminate the Ord+-- constraint. If so, remove Ord tr from 'time' in Behavior. instance Monoid t => Copointed (ReactiveG t) where -- extract = extract . rat
src/FRP/Reactive/Reactive.hs view
@@ -94,12 +94,18 @@ -- | Access occurrence times in an event. See 'withTimeGE' for more -- general notions of time.-withTimeE :: Event a -> Event (a, TimeT)+-- +-- > withTimeE :: Event a -> Event (a, TimeT)+withTimeE :: Ord t =>+ EventG (Improving t) d -> EventG (Improving t) (d, t) 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_ :: Event a -> Event TimeT+withTimeE_ :: Ord t =>+ EventG (Improving t) d -> EventG (Improving t) t withTimeE_ = (result.fmap) snd withTimeE timeT :: Ord t => Time t -> t@@ -337,7 +343,7 @@ -- fix the implementation, rather than changing the semantics. (No -- "delayed integral".) -sumR :: AdditiveGroup v => Event v -> Reactive v+sumR :: Ord t => AdditiveGroup v => EventG t v -> ReactiveG t v sumR = scanlR (^+^) zeroV