@@ -1,4 +1,3 @@
1−
21{-# LANGUAGE CPP #-}
32{-# LANGUAGE FlexibleContexts #-}
43{-# LANGUAGE FlexibleInstances #-}
@@ -14,7 +13,71 @@
1413{-# LANGUAGE TypeOperators #-}
1514{-# LANGUAGE UndecidableInstances #-}
1615
17−module Reflex.Class where
16+-- | This module contains the Reflex interface, as well as a variety of
17+-- convenience functions for working with 'Event's, 'Behavior's, and other
18+-- signals.
19+module Reflex.Class
20+ ( -- * Primitives
21+ Reflex (..)
22+ , MonadSample (..)
23+ , MonadHold (..)
24+ -- ** 'fan'-related types
25+ , EventSelector (..)
26+ -- ** 'Incremental'-related types
27+ , Patch (..)
28+ , PatchDMap (..)
29+ -- * Convenience functions
30+ , constDyn
31+ , pushAlways
32+ -- ** Combining 'Event's
33+ , leftmost
34+ , mergeMap
35+ , mergeList
36+ , mergeWith
37+ -- ** Breaking up 'Event's
38+ , splitE
39+ , fanMap
40+ , dmapToThese
41+ , EitherTag (..)
42+ , eitherToDSum
43+ , dsumToEither
44+ -- ** Switching between 'Event's
45+ , switchPromptly
46+ -- ** Using 'Event's to sample 'Behavior's
47+ , tag
48+ , attach
49+ , attachWith
50+ , attachWithMaybe
51+ -- ** Blocking an 'Event' based on a 'Behavior'
52+ , gate
53+ -- ** Combining 'Dynamic's
54+ , distributeDMapOverDynPure
55+ , zipDyn
56+ , zipDynWith
57+ -- ** Accumulating state
58+ , Accumulator (..)
59+ , mapAccum_
60+ , mapAccumM_
61+ , mapAccumMaybeM_
62+ , zipListWithEvent
63+ , headE
64+ , tailE
65+ , switcher
66+ -- * Debugging functions
67+ , traceEvent
68+ , traceEventWith
69+ -- * Unsafe functions
70+ , unsafeDynamic
71+ -- * 'FunctorMaybe'
72+ , FunctorMaybe (..)
73+ , fforMaybe
74+ , ffilter
75+ -- * Miscellaneous convenience functions
76+ , ffor
77+ -- * Deprecated functions
78+ , onceE
79+ , appendEvents
80+ ) where
1881
1982import Control.Applicative
2083import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
@@ -52,54 +115,76 @@ import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
52115
53116import Debug.Trace (trace)
54117
118+-- | The 'Reflex' class contains all the primitive functionality needed for
119+-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
120+-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
121+-- and the type of the timeline determines the FRP engine to be used. For most
122+-- purposes, the 'Reflex.Spider' implementation is recommended.
55123class ( MonadHold t (PushM t)
56124 , MonadSample t (PullM t)
57125 , MonadFix (PushM t)
58126 , Functor (Event t)
59127 , Functor (Behavior t)
60128 , Functor (Dynamic t)
61− , Applicative (Dynamic t) -- Necessary for GHC <=7.8
129+ , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
62130 , Monad (Dynamic t)
63131 ) => Reflex t where
64− -- | A container for a value that can change over time. Behaviors can be sampled at will, but it is not possible to be notified when they change
132+ -- | A container for a value that can change over time. 'Behavior's can be
133+ -- sampled at will, but it is not possible to be notified when they change
65134 data Behavior t :: * -> *
66− -- | A stream of occurrences. During any given frame, an Event is either occurring or not occurring; if it is occurring, it will contain a value of the given type (its "occurrence type")
135+ -- | A stream of occurrences. During any given frame, an 'Event' is either
136+ -- occurring or not occurring; if it is occurring, it will contain a value of
137+ -- the given type (its "occurrence type")
67138 data Event t :: * -> *
68− -- | A container for a value that can change over time and allows notifications on changes.
69− -- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
70− -- change if and only if the Event fires.
139+ -- | A container for a value that can change over time and allows
140+ -- notifications on changes. Basically a combination of a 'Behavior' and an
141+ -- 'Event', with a rule that the 'Behavior' will change if and only if the
142+ -- 'Event' fires.
71143 data Dynamic t :: * -> *
72− -- | A container for a value that can change over time and allows notifications on changes.
73− -- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
74− -- change if and only if the Event fires.
144+ -- | A container for a value that can change over time and allows
145+ -- notifications on changes. Basically a combination of a 'Behavior' and an
146+ -- 'Event', with a rule that the 'Behavior' will change if and only if the
147+ -- 'Event' fires.
75148 data Incremental t :: (* -> *) -> * -> *
76− -- | An Event with no occurrences
149+ -- | An 'Event' with no occurrences
77150 never :: Event t a
78− -- | Create a Behavior that always has the given value
79− constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of Behaviors doesn't have a negative performance impact
80− -- | Create an Event from another Event; the provided function can sample Behaviors and hold Events, and use the results to produce a occurring (Just) or non-occurring (Nothing) result
151+ -- | Create a 'Behavior' that always has the given value
152+ constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of 'Behavior's doesn't have a negative performance impact
153+ -- | Create an 'Event' from another 'Event'; the provided function can sample
154+ -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
155+ -- (Just) or non-occurring (Nothing) result
81156 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
82157 -- | A monad for doing complex push-based calculations efficiently
83158 type PushM t :: * -> *
84− -- | Create a Behavior by reading from other Behaviors; the result will be recomputed whenever any of the read Behaviors changes
159+ -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
160+ -- recomputed whenever any of the read 'Behavior's changes
85161 pull :: PullM t a -> Behavior t a
86162 -- | A monad for doing complex pull-based calculations efficiently
87163 type PullM t :: * -> *
88− -- | Merge a collection of events; the resulting Event will only occur if at least one input event is occuring, and will contain all of the input keys that are occurring simultaneously
164+ -- | Merge a collection of events; the resulting 'Event' will only occur if at
165+ -- least one input event is occuring, and will contain all of the input keys
166+ -- that are occurring simultaneously
89167 merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
90− -- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
168+ -- | Efficiently fan-out an event to many destinations. This function should
169+ -- be partially applied, and then the result applied repeatedly to create
170+ -- child events
91171 fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
92− -- | Create an Event that will occur whenever the currently-selected input Event occurs
172+ -- | Create an 'Event' that will occur whenever the currently-selected input
173+ -- 'Event' occurs
93174 switch :: Behavior t (Event t a) -> Event t a
94− -- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
175+ -- | Create an 'Event' that will occur whenever the input event is occurring
176+ -- and its occurrence value, another 'Event', is also occurring
95177 coincidence :: Event t (Event t a) -> Event t a
96178 -- | Extract the 'Behavior' of a 'Dynamic'.
97179 current :: Dynamic t a -> Behavior t a
98180 -- | Extract the 'Event' of the 'Dynamic'.
99181 updated :: Dynamic t a -> Event t a
100− -- | Create a new 'Dynamic'. The given PullM must always return the most recent firing of the given Event, if any.
182+ -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
183+ -- recent firing of the given 'Event', if any.
101184 unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
102− -- | Create a new 'Incremental'. The given PullM's value must always change in the same way that the accumulated application of patches would change that value.
185+ -- | Create a new 'Incremental'. The given 'PullM''s value must always change
186+ -- in the same way that the accumulated application of patches would change
187+ -- that value.
103188 unsafeBuildIncremental :: Patch p => PullM t a -> Event t (p a) -> Incremental t p a
104189 -- | Create a merge whose parents can change over time
105190 mergeIncremental :: GCompare k => Incremental t PatchDMap (DMap k (Event t)) -> Event t (DMap k Identity)
@@ -110,12 +195,16 @@ class ( MonadHold t (PushM t)
110195 -- | Convert an 'Incremental' to a 'Dynamic'
111196 incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
112197
198+-- | A 'Patch' type represents a kind of change made to a datastructure.
113199class Patch p where
114− apply :: p a -> a -> Maybe a -- Return Nothing if no change is necessary
200+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
201+ -- 'Nothing'.
202+ apply :: p a -> a -> Maybe a
115203
116204instance Patch Identity where
117205 apply (Identity a) _ = Just a
118206
207+-- | A set of changes to a 'DMap'. Only DMap types are allowed for @a@.
119208data PatchDMap a where
120209 PatchDMap :: GCompare k => DMap k (Compose Maybe v) -> PatchDMap (DMap k v)
121210
@@ -143,27 +232,55 @@ instance Patch PatchDMap where
143232 Nothing -> Just $ Constant ()
144233 Just _ -> Nothing
145234
235+-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
236+-- _must_ change when and only when the 'Event' fires, such that the
237+-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
238+-- if this is not the case, the resulting 'Dynamic' will behave
239+-- nondeterministically.
146240unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
147241unsafeDynamic = unsafeBuildDynamic . sample
148242
243+-- | Construct a 'Dynamic' value that never changes
149244constDyn :: Reflex t => a -> Dynamic t a
150245constDyn = pure
151246
247+-- | 'MonadSample' designates monads that can read the current value of a
248+-- 'Behavior'. This includes both 'PullM' and 'PushM'.
152249class (Applicative m, Monad m) => MonadSample t m | m -> t where
153− -- | Get the current value in the Behavior
250+ -- | Get the current value in the 'Behavior'
154251 sample :: Behavior t a -> m a
155252
253+-- | 'MonadHold' designates monads that can create new 'Behavior's based on
254+-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
255+-- is required to create any stateful computations with Reflex.
156256class MonadSample t m => MonadHold t m where
157− -- | Create a new Behavior whose value will initially be equal to the given value and will be updated whenever the given Event occurs. The update takes effect immediately after the Event occurs; if the occurrence that sets the Behavior (or one that is simultaneous with it) is used to sample the Behavior, it will see the *old* value of the Behavior, not the new one.
257+ -- | Create a new 'Behavior' whose value will initially be equal to the given
258+ -- value and will be updated whenever the given 'Event' occurs. The update
259+ -- takes effect immediately after the 'Event' occurs; if the occurrence that
260+ -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
261+ -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
262+ -- one.
158263 hold :: a -> Event t a -> m (Behavior t a)
159264 -- | Create a 'Dynamic' value using the given initial value that changes every
160265 -- time the 'Event' occurs.
161266 holdDyn :: a -> Event t a -> m (Dynamic t a)
162− -- | Create an 'Incremental' value using the given initial value that changes every
163− -- time the 'Event' occurs.
267+ -- | Create an 'Incremental' value using the given initial value that changes
268+ -- every time the 'Event' occurs.
164269 holdIncremental :: Patch p => a -> Event t (p a) -> m (Incremental t p a)
165270
166−newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
271+-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
272+-- key. This is much more efficient than filtering for each key with
273+-- 'fmapMaybe'.
274+newtype EventSelector t k = EventSelector
275+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
276+ -- determined by the type of the key, so this can be used to fan-out
277+ -- 'Event's whose sub-'Event's have different types.
278+ --
279+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
280+ -- (but equivalent to) using 'fmapMaybe' to select only the relevant
281+ -- occurrences of an 'Event'.
282+ select :: forall a. k a -> Event t a
283+ }
167284
168285--------------------------------------------------------------------------------
169286-- Instances
@@ -221,8 +338,8 @@ instance MonadHold t m => MonadHold t (ContT r m) where
221338-- Convenience functions
222339--------------------------------------------------------------------------------
223340
224−-- | Create an Event from another Event.
225−-- The provided function can sample 'Behavior's and hold 'Event's.
341+-- | Create an 'Event' from another 'Event'. The provided function can sample
342+-- 'Behavior's and hold 'Event's.
226343pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
227344pushAlways f = push (fmap Just . f)
228345
@@ -255,7 +372,7 @@ instance (Reflex t, IsString a) => IsString (Behavior t a) where
255372
256373instance Reflex t => Monad (Behavior t) where
257374 a >>= f = pull $ sample a >>= sample . f
258− -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine Behaviors other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
375+ -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine 'Behavior's other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
259376 return = constant
260377 fail = error "Monad (Behavior t) does not support fail"
261378
@@ -305,11 +422,11 @@ ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
305422instance Reflex t => Alt (Event t) where
306423 ev1 <!> ev2 = leftmost [ev1, ev2]
307424
308−-- | Event intersection (convenient interface to 'coincidence').
425+-- | 'Event' intersection (convenient interface to 'coincidence').
309426instance Reflex t => Apply (Event t) where
310427 evf <.> evx = coincidence (fmap (<$> evx) evf)
311428
312−-- | Event intersection (convenient interface to 'coincidence').
429+-- | 'Event' intersection (convenient interface to 'coincidence').
313430instance Reflex t => Bind (Event t) where
314431 evx >>- f = coincidence (f <$> evx)
315432 join = coincidence
@@ -329,27 +446,23 @@ instance Reflex t => Plus (Event t) where
329446tag :: Reflex t => Behavior t b -> Event t a -> Event t b
330447tag b = pushAlways $ \_ -> sample b
331448
332−-- | Create a new 'Event' that combines occurences of supplied 'Event'
333−-- with the current value of the 'Behavior'.
449+-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
450+-- current value of the 'Behavior'.
334451attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
335452attach = attachWith (,)
336453
337−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
338−-- by combining it with the current value of the 'Behavior'.
454+-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
455+-- combining it with the current value of the 'Behavior'.
339456attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
340457attachWith f = attachWithMaybe $ \a b -> Just $ f a b
341458
342−-- | Create a new 'Event' by combining each occurence with the current
343−-- value of the 'Behavior'. The occurrence is discarded if the combining function
344−-- returns Nothing
459+-- | Create a new 'Event' by combining each occurence with the current value of
460+-- the 'Behavior'. The occurrence is discarded if the combining function returns
461+-- Nothing
345462attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
346463attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
347464
348−-- | Alias for 'headE'
349−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
350−onceE = headE
351−
352−-- | Create a new 'Event' that only occurs on the first occurence of
465+-- | Create a new 'Event' that only occurs only once, on the first occurence of
353466-- the supplied 'Event'.
354467headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
355468headE e = do
@@ -358,38 +471,38 @@ headE e = do
358471 e' `seq` return ()
359472 return e'
360473
361−-- | Create a new 'Event' that occurs on all but the first occurence
362−-- of the supplied 'Event'.
474+-- | Create a new 'Event' that occurs on all but the first occurence of the
475+-- supplied 'Event'.
363476tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
364477tailE e = snd <$> headTailE e
365478
366−-- | Create a tuple of two 'Event's with the first one occuring only
367−-- the first time the supplied 'Event' occurs and the second occuring
368−-- on all but the first occurence.
479+-- | Create a tuple of two 'Event's with the first one occuring only the first
480+-- time the supplied 'Event' occurs and the second occuring on all but the first
481+-- occurence.
369482headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
370483headTailE e = do
371484 eHead <- headE e
372485 be <- hold never $ fmap (const e) eHead
373486 return (eHead, switch be)
374487
375−-- | Split the supplied 'Event' into two individual 'Event's occuring
376−-- at the same time with the respective values from the tuple.
488+-- | Split the supplied 'Event' into two individual 'Event's occuring at the
489+-- same time with the respective values from the tuple.
377490splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
378491splitE e = (fmap fst e, fmap snd e)
379492
380493-- | Print the supplied 'String' and the value of the 'Event' on each
381494-- occurence. This should /only/ be used for debugging.
382495--
383−-- Note: As with Debug.Trace.trace, the message will only be printed if
384−-- the 'Event' is actually used.
496+-- Note: As with Debug.Trace.trace, the message will only be printed if the
497+-- 'Event' is actually used.
385498traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
386499traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
387500
388−-- | Print the output of the supplied function on each occurence of
389−-- the 'Event'. This should /only/ be used for debugging.
501+-- | Print the output of the supplied function on each occurence of the
502+-- 'Event'. This should /only/ be used for debugging.
390503--
391−-- Note: As with Debug.Trace.trace, the message will only be printed if
392−-- the 'Event' is actually used.
504+-- Note: As with Debug.Trace.trace, the message will only be printed if the
505+-- 'Event' is actually used.
393506traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
394507traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
395508
@@ -441,12 +554,6 @@ dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
441554 (Nothing, Just (Identity b)) -> Just $ That b
442555 (Just (Identity a), Just (Identity b)) -> Just $ These a b
443556
444−-- | Create a new 'Event' that occurs if at least one of the supplied
445−-- 'Event's occurs. If both occur at the same time they are combined
446−-- using 'mappend'.
447−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
448−appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
449−
450557{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
451558{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
452559sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
@@ -469,9 +576,9 @@ instance (Semigroup a, Reflex t) => Monoid (Event t a) where
469576 mappend = (<>)
470577 mconcat = fmap sconcat . mergeList
471578
472−-- | Create a new 'Event' that occurs if at least one of the 'Event's
473−-- in the list occurs. If multiple occur at the same time they are
474−-- folded from the left with the given function.
579+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
580+-- list occurs. If multiple occur at the same time they are folded from the left
581+-- with the given function.
475582mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
476583mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
477584 . merge
@@ -479,31 +586,32 @@ mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v)
479586 . map (\(k, v) -> Const2 k :=> v)
480587 $ zip [0 :: Int ..] es
481588
482−-- | Create a new 'Event' that occurs if at least one of the 'Event's
483−-- in the list occurs. If multiple occur at the same time the value is
484−-- the value of the leftmost event.
589+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
590+-- list occurs. If multiple occur at the same time the value is the value of the
591+-- leftmost event.
485592leftmost :: Reflex t => [Event t a] -> Event t a
486593leftmost = mergeWith const
487594
488−-- | Create a new 'Event' that occurs if at least one of the 'Event's
489−-- in the list occurs and has a list of the values of all 'Event's
490−-- occuring at that time.
595+-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
596+-- list occurs and has a list of the values of all 'Event's occuring at that
597+-- time.
491598mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
492599mergeList [] = never
493600mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
494601
495−-- | Create a new 'Event' combining the map of 'Event's into an
496−-- 'Event' that occurs if at least one of them occurs and has a map of
497−-- values of all 'Event's occuring at that time.
602+-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
603+-- occurs if at least one of them occurs and has a map of values of all 'Event's
604+-- occuring at that time.
498605mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
499606mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
500607
501−-- | Split the event into an 'EventSelector' that allows efficient
502−-- selection of the individual 'Event's.
608+-- | Split the event into an 'EventSelector' that allows efficient selection of
609+-- the individual 'Event's.
503610fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
504611fanMap = fan . fmap mapToDMap
505612
506−-- | Switches to the new event whenever it receives one; the new event is used immediately, on the same frame that it is switched to
613+-- | Switches to the new event whenever it receives one; the new event is used
614+-- immediately, on the same frame that it is switched to
507615switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
508616switchPromptly ea0 eea = do
509617 bea <- hold ea0 eea
@@ -515,17 +623,24 @@ instance Reflex t => Align (Event t) where
515623 nil = never
516624 align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
517625
518−-- | Create a new 'Event' that only occurs if the supplied 'Event'
519−-- occurs and the 'Behavior' is true at the time of occurence.
626+-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
627+-- the 'Behavior' is true at the time of occurence.
520628gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
521629gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
522630
523−-- | Create a new behavior given a starting behavior and switch to a the
524−-- behvior carried by the event when it fires.
631+-- | Create a new behavior given a starting behavior and switch to a the behvior
632+-- carried by the event when it fires.
525633switcher :: (Reflex t, MonadHold t m)
526634 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
527635switcher b eb = pull . (sample <=< sample) <$> hold b eb
528636
637+-- | Combine two 'Dynamic's. The result will change whenever either (or both)
638+-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
639+zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
640+zipDyn = zipDynWith (,)
641+
642+-- | Combine two 'Dynamic's with a combining function. The result will change
643+-- whenever either (or both) input 'Dynamic' changes.
529644zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
530645zipDynWith f da db =
531646 let eab = align (updated da) (updated db)
@@ -546,18 +661,27 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
546661 mempty = constDyn mempty
547662 mappend = zipDynWith mappend
548663
664+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
665+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
666+-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
549667distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
550668distributeDMapOverDynPure dm = case DMap.toList dm of
551669 [] -> constDyn DMap.empty
552670 [k :=> v] -> fmap (DMap.singleton k . Identity) v
553671 _ ->
554672 let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
555− edmPre = merge $ rewrapDMap updated dm
673+ edmPre = merge $ DMap.map updated dm
556674 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
557675 olds <- sample $ current result
558676 return $ DMap.unionWithKey (\_ _ new -> new) olds news
559677 in result
560678
679+--------------------------------------------------------------------------------
680+-- Accumulator
681+--------------------------------------------------------------------------------
682+
683+-- | An 'Accumulator' type can be built by accumulating occurrences of an
684+-- 'Event'.
561685class Reflex t => Accumulator t f | f -> t where
562686 accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
563687 accum f = accumMaybe $ \v o -> Just $ f v o
@@ -574,21 +698,30 @@ class Reflex t => Accumulator t f | f -> t where
574698 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
575699 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
576700
701+-- | Accumulate occurrences of an 'Event', producing an output occurrence each
702+-- time. Discard the underlying 'Accumulator'.
577703mapAccum_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
578704mapAccum_ f z e = do
579705 (_ :: Behavior t a, result) <- mapAccum f z e
580706 return result
581707
708+-- | Accumulate occurrences of an 'Event', possibly producing an output
709+-- occurrence each time. Discard the underlying 'Accumulator'.
582710mapAccumMaybe_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
583711mapAccumMaybe_ f z e = do
584712 (_ :: Behavior t a, result) <- mapAccumMaybe f z e
585713 return result
586714
715+-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
716+-- an output occurrence each time. Discard the underlying 'Accumulator'.
587717mapAccumM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
588718mapAccumM_ f z e = do
589719 (_ :: Behavior t a, result) <- mapAccumM f z e
590720 return result
591721
722+-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
723+-- producing an output occurrence each time. Discard the underlying
724+-- 'Accumulator'.
592725mapAccumMaybeM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
593726mapAccumMaybeM_ f z e = do
594727 (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
@@ -619,12 +752,27 @@ instance Reflex t => Accumulator t (Event t) where
619752 accumMaybeM f z e = updated <$> accumMaybeM f z e
620753 mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
621754
622−-- | Create a new 'Event' by combining each occurence with the next value
623−-- of the list using the supplied function. If the list runs out of items,
624−-- all subsequent 'Event' occurrences will be ignored.
755+-- | Create a new 'Event' by combining each occurence with the next value of the
756+-- list using the supplied function. If the list runs out of items, all
757+-- subsequent 'Event' occurrences will be ignored.
625758zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
626759zipListWithEvent f l e = do
627760 let f' a b = case a of
628761 h:t -> (Just t, Just $ f h b)
629762 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
630763 mapAccumMaybe_ f' l e
764+
765+--------------------------------------------------------------------------------
766+-- Deprecated functions
767+--------------------------------------------------------------------------------
768+
769+-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
770+-- occurs. If both occur at the same time they are combined using 'mappend'.
771+{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
772+appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
773+appendEvents = alignWith $ mergeThese mappend
774+
775+-- | Alias for 'headE'
776+{-# DEPRECATED onceE "Use 'headE' instead" #-}
777+onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
778+onceE = headE