2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE NoMonomorphismRestriction #-}
9{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RecursiveDo #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE UndecidableInstances #-}
15module Reflex.Class where
17import Control.Applicative
18import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
19import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
20import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
21import Control.Monad.Trans.Cont (ContT ())
22import Control.Monad.Trans.Except (ExceptT ())
23import Control.Monad.Trans.RWS (RWST ())
24import Control.Monad.Trans.Writer (WriterT ())
27import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
28import qualified Data.Dependent.Map as DMap
29import Data.Dependent.Sum (ShowTag (..))
31import Data.Functor.Compose
32import Data.Functor.Constant
33import Data.Functor.Misc
34import Data.GADT.Compare ((:~:) (..), GEq (..))
35import Data.GADT.Show (GShow (..))
36import Data.List.NonEmpty (NonEmpty (..))
39import Data.Monoid hiding ((<>))
40import Data.Semigroup (Semigroup, sconcat, stimes, stimesIdempotentMonoid, (<>))
42import Data.Traversable
44-- Note: must come last to silence warnings due to AMP on GHC < 7.10
45import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
47import Debug.Trace (trace)
49class ( MonadHold t (PushM t)
50 , MonadSample t (PullM t)
53 , Functor (Behavior t)
55 , Applicative (Dynamic t) -- Necessary for GHC <=7.8
58 -- | 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
59 data Behavior t :: * -> *
60 -- | 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")
61 data Event t :: * -> *
62 -- | A container for a value that can change over time and allows notifications on changes.
63 -- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
64 -- change if and only if the Event fires.
65 data Dynamic t :: * -> *
66 -- | A container for a value that can change over time and allows notifications on changes.
67 -- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
68 -- change if and only if the Event fires.
69 data Incremental t :: (* -> *) -> * -> *
70 -- | An Event with no occurrences
72 -- | Create a Behavior that always has the given value
73 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
74 -- | 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
75 push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
76 -- | A monad for doing complex push-based calculations efficiently
77 type PushM t :: * -> *
78 -- | Create a Behavior by reading from other Behaviors; the result will be recomputed whenever any of the read Behaviors changes
79 pull :: PullM t a -> Behavior t a
80 -- | A monad for doing complex pull-based calculations efficiently
81 type PullM t :: * -> *
82 -- | 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
83 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
84 -- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
85 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
86 -- | Create an Event that will occur whenever the currently-selected input Event occurs
87 switch :: Behavior t (Event t a) -> Event t a
88 -- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
89 coincidence :: Event t (Event t a) -> Event t a
90 -- | Extract the 'Behavior' of a 'Dynamic'.
91 current :: Dynamic t a -> Behavior t a
92 -- | Extract the 'Event' of the 'Dynamic'.
93 updated :: Dynamic t a -> Event t a
94 -- | Create a new 'Dynamic'. The given PullM must always return the most recent firing of the given Event, if any.
95 unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
96 -- | 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.
97 unsafeBuildIncremental :: Patch p => PullM t a -> Event t (p a) -> Incremental t p a
98 -- | Create a merge whose parents can change over time
99 mergeIncremental :: GCompare k => Incremental t PatchDMap (DMap k (Event t)) -> Event t (DMap k Identity)
100 -- | Extract the 'Behavior' component of an 'Incremental'
101 currentIncremental :: Patch p => Incremental t p a -> Behavior t a
102 -- | Extract the 'Event' component of an 'Incremental'
103 updatedIncremental :: Patch p => Incremental t p a -> Event t (p a)
104 -- | Convert an 'Incremental' to a 'Dynamic'
105 incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
108 apply :: p a -> a -> Maybe a -- Return Nothing if no change is necessary
110instance Patch Identity where
111 apply (Identity a) _ = Just a
113data PatchDMap a where
114 PatchDMap :: GCompare k => DMap k (Compose Maybe v) -> PatchDMap (DMap k v)
116instance GCompare k => Semigroup (PatchDMap (DMap k v)) where
117 PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
118 -- PatchDMap is idempotent, so stimes n is id for every n
119#if MIN_VERSION_semigroups(0,17,0)
120 stimes = stimesIdempotentMonoid
122 times1p n x = case compare n 0 of
123 LT -> error "stimesIdempotentMonoid: negative multiplier"
128instance GCompare k => Monoid (PatchDMap (DMap k v)) where
129 mempty = PatchDMap mempty
132instance Patch PatchDMap where
133 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
134 where insertions = DMap.mapMaybeWithKey (const $ getCompose) diff
135 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getCompose) diff
136 nothingToJust = \case
137 Nothing -> Just $ Constant ()
140unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
141unsafeDynamic = unsafeBuildDynamic . sample
143constDyn :: Reflex t => a -> Dynamic t a
146class (Applicative m, Monad m) => MonadSample t m | m -> t where
147 -- | Get the current value in the Behavior
148 sample :: Behavior t a -> m a
150class MonadSample t m => MonadHold t m where
151 -- | 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.
152 hold :: a -> Event t a -> m (Behavior t a)
153 -- | Create a 'Dynamic' value using the given initial value that changes every
154 -- time the 'Event' occurs.
155 holdDyn :: a -> Event t a -> m (Dynamic t a)
156 -- | Create an 'Incremental' value using the given initial value that changes every
157 -- time the 'Event' occurs.
158 holdIncremental :: Patch p => a -> Event t (p a) -> m (Incremental t p a)
160newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
162--------------------------------------------------------------------------------
164--------------------------------------------------------------------------------
166instance MonadSample t m => MonadSample t (ReaderT r m) where
167 sample = lift . sample
169instance MonadHold t m => MonadHold t (ReaderT r m) where
170 hold a0 = lift . hold a0
171 holdDyn a0 = lift . holdDyn a0
172 holdIncremental a0 = lift . holdIncremental a0
174instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
175 sample = lift . sample
177instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
178 hold a0 = lift . hold a0
179 holdDyn a0 = lift . holdDyn a0
180 holdIncremental a0 = lift . holdIncremental a0
182instance MonadSample t m => MonadSample t (StateT s m) where
183 sample = lift . sample
185instance MonadHold t m => MonadHold t (StateT s m) where
186 hold a0 = lift . hold a0
187 holdDyn a0 = lift . holdDyn a0
188 holdIncremental a0 = lift . holdIncremental a0
190instance MonadSample t m => MonadSample t (ExceptT e m) where
191 sample = lift . sample
193instance MonadHold t m => MonadHold t (ExceptT e m) where
194 hold a0 = lift . hold a0
195 holdDyn a0 = lift . holdDyn a0
196 holdIncremental a0 = lift . holdIncremental a0
198instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
199 sample = lift . sample
201instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
202 hold a0 = lift . hold a0
203 holdDyn a0 = lift . holdDyn a0
204 holdIncremental a0 = lift . holdIncremental a0
206instance MonadSample t m => MonadSample t (ContT r m) where
207 sample = lift . sample
209instance MonadHold t m => MonadHold t (ContT r m) where
210 hold a0 = lift . hold a0
211 holdDyn a0 = lift . holdDyn a0
212 holdIncremental a0 = lift . holdIncremental a0
214--------------------------------------------------------------------------------
215-- Convenience functions
216--------------------------------------------------------------------------------
218-- | Create an Event from another Event.
219-- The provided function can sample 'Behavior's and hold 'Event's.
220pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
221pushAlways f = push (fmap Just . f)
223-- | Flipped version of 'fmap'.
224ffor :: Functor f => f a -> (a -> b) -> f b
227instance Reflex t => Functor (Behavior t) where
228 fmap f = pull . fmap f . sample
230instance Reflex t => Applicative (Behavior t) where
232 f <*> x = pull $ sample f `ap` sample x
236instance Reflex t => Monad (Behavior t) where
237 a >>= f = pull $ sample a >>= sample . f
238 -- 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.
240 fail = error "Monad (Behavior t) does not support fail"
242instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
243 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
244 sconcat = pull . fmap sconcat . mapM sample
245#if MIN_VERSION_semigroups(0,17,0)
246 stimes n = fmap $ stimes n
248 times1p n = fmap $ times1p n
251instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
252 mempty = constant mempty
253 mappend a b = pull $ liftM2 mappend (sample a) (sample b)
254 mconcat = pull . fmap mconcat . mapM sample
256--TODO: See if there's a better class in the standard libraries already
257-- | A class for values that combines filtering and mapping using 'Maybe'.
258class FunctorMaybe f where
259 -- | Combined mapping and filtering function.
260 fmapMaybe :: (a -> Maybe b) -> f a -> f b
262instance FunctorMaybe [] where
263 fmapMaybe f = catMaybes . fmap f
265-- | Flipped version of 'fmapMaybe'.
266fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
267fforMaybe = flip fmapMaybe
269-- | Filter 'f a' using the provided predicate.
270-- Relies on 'fforMaybe'.
271ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
272ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
274instance Reflex t => FunctorMaybe (Event t) where
275 fmapMaybe f = push $ return . f
277instance Reflex t => Functor (Event t) where
278 fmap f = fmapMaybe $ Just . f
280-- | Replace each occurrence value of the 'Event' with the value of the
281-- 'Behavior' at the time of that occurrence.
282tag :: Reflex t => Behavior t b -> Event t a -> Event t b
283tag b = pushAlways $ \_ -> sample b
285-- | Create a new 'Event' that combines occurences of supplied 'Event'
286-- with the current value of the 'Behavior'.
287attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
288attach = attachWith (,)
290-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
291-- by combining it with the current value of the 'Behavior'.
292attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
293attachWith f = attachWithMaybe $ \a b -> Just $ f a b
295-- | Create a new 'Event' by combining each occurence with the current
296-- value of the 'Behavior'. The occurrence is discarded if the combining function
298attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
299attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
301-- | Alias for 'headE'
302onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
305-- | Create a new 'Event' that only occurs on the first occurence of
306-- the supplied 'Event'.
307headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
309 rec be <- hold e $ fmap (const never) e'
314-- | Create a new 'Event' that occurs on all but the first occurence
315-- of the supplied 'Event'.
316tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
317tailE e = snd <$> headTailE e
319-- | Create a tuple of two 'Event's with the first one occuring only
320-- the first time the supplied 'Event' occurs and the second occuring
321-- on all but the first occurence.
322headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
325 be <- hold never $ fmap (const e) eHead
326 return (eHead, switch be)
328-- | Split the supplied 'Event' into two individual 'Event's occuring
329-- at the same time with the respective values from the tuple.
330splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
331splitE e = (fmap fst e, fmap snd e)
333-- | Print the supplied 'String' and the value of the 'Event' on each
334-- occurence. This should /only/ be used for debugging.
336-- Note: As with Debug.Trace.trace, the message will only be printed if
337-- the 'Event' is actually used.
338traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
339traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
341-- | Print the output of the supplied function on each occurence of
342-- the 'Event'. This should /only/ be used for debugging.
344-- Note: As with Debug.Trace.trace, the message will only be printed if
345-- the 'Event' is actually used.
346traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
347traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
349-- | Tag type for 'Either' to use it as a 'DSum'.
350data EitherTag l r a where
351 LeftTag :: EitherTag l r l
352 RightTag :: EitherTag l r r
354instance GEq (EitherTag l r) where
355 geq a b = case (a, b) of
356 (LeftTag, LeftTag) -> Just Refl
357 (RightTag, RightTag) -> Just Refl
360instance GCompare (EitherTag l r) where
361 gcompare a b = case (a, b) of
362 (LeftTag, LeftTag) -> GEQ
363 (LeftTag, RightTag) -> GLT
364 (RightTag, LeftTag) -> GGT
365 (RightTag, RightTag) -> GEQ
367instance GShow (EitherTag l r) where
368 gshowsPrec _ a = case a of
369 LeftTag -> showString "LeftTag"
370 RightTag -> showString "RightTag"
372instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
373 showTaggedPrec t n (Identity a) = case t of
374 LeftTag -> showsPrec n a
375 RightTag -> showsPrec n a
377-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
378eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
380 Left a -> (LeftTag :=> Identity a)
381 Right b -> (RightTag :=> Identity b)
383-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
384dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
386 (LeftTag :=> Identity a) -> Left a
387 (RightTag :=> Identity b) -> Right b
389-- | Extract the values of a 'DMap' of 'EitherTag's.
390dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
391dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
392 (Nothing, Nothing) -> Nothing
393 (Just (Identity a), Nothing) -> Just $ This a
394 (Nothing, Just (Identity b)) -> Just $ That b
395 (Just (Identity a), Just (Identity b)) -> Just $ These a b
397-- | Create a new 'Event' that occurs if at least one of the supplied
398-- 'Event's occurs. If both occur at the same time they are combined
400appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
401appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
403{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
404{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
405sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
406sequenceThese t = case t of
407 This ma -> liftM This ma
408 These ma mb -> liftM2 These ma mb
409 That mb -> liftM That mb
411instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
412 (<>) = alignWith (mergeThese (<>))
413 sconcat = fmap sconcat . mergeList . toList
414#if MIN_VERSION_semigroups(0,17,0)
415 stimes n = fmap $ stimes n
417 times1p n = fmap $ times1p n
420instance (Semigroup a, Reflex t) => Monoid (Event t a) where
423 mconcat = fmap sconcat . mergeList
425-- | Create a new 'Event' that occurs if at least one of the 'Event's
426-- in the list occurs. If multiple occur at the same time they are
427-- folded from the left with the given function.
428mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
429mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
431 . DMap.fromDistinctAscList
432 . map (\(k, v) -> Const2 k :=> v)
433 $ zip [0 :: Int ..] es
435-- | Create a new 'Event' that occurs if at least one of the 'Event's
436-- in the list occurs. If multiple occur at the same time the value is
437-- the value of the leftmost event.
438leftmost :: Reflex t => [Event t a] -> Event t a
439leftmost = mergeWith const
441-- | Create a new 'Event' that occurs if at least one of the 'Event's
442-- in the list occurs and has a list of the values of all 'Event's
443-- occuring at that time.
444mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
446mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
448-- | Create a new 'Event' combining the map of 'Event's into an
449-- 'Event' that occurs if at least one of them occurs and has a map of
450-- values of all 'Event's occuring at that time.
451mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
452mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
454-- | Split the event into an 'EventSelector' that allows efficient
455-- selection of the individual 'Event's.
456fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
457fanMap = fan . fmap mapToDMap
459-- | Switches to the new event whenever it receives one; the new event is used immediately, on the same frame that it is switched to
460switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
461switchPromptly ea0 eea = do
463 let eLag = switch bea
464 eCoincidences = coincidence eea
465 return $ leftmost [eCoincidences, eLag]
467instance Reflex t => Align (Event t) where
469 align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
471-- | Create a new 'Event' that only occurs if the supplied 'Event'
472-- occurs and the 'Behavior' is true at the time of occurence.
473gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
474gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
476-- | Create a new behavior given a starting behavior and switch to a the
477-- behvior carried by the event when it fires.
478switcher :: (Reflex t, MonadHold t m)
479 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
480switcher b eb = pull . (sample <=< sample) <$> hold b eb
482zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
484 let eab = align (updated da) (updated db)
485 ec = flip push eab $ \o -> do
488 b <- sample $ current db
491 a <- sample $ current da
493 These a b -> return (a, b)
494 return $ Just $ f a b
495 in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
497instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
498 mconcat = fmap (mconcat . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
499 mempty = constDyn mempty
500 mappend = zipDynWith mappend
502distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
503distributeDMapOverDynPure dm = case DMap.toList dm of
504 [] -> constDyn DMap.empty
505 [k :=> v] -> fmap (DMap.singleton k . Identity) v
507 let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
508 edmPre = merge $ rewrapDMap updated dm
509 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
510 olds <- sample $ current result
511 return $ DMap.unionWithKey (\_ _ new -> new) olds news
514class Reflex t => Accumulator t f | f -> t where
515 accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
516 accum f = accumMaybe $ \v o -> Just $ f v o
517 accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
518 accumM f = accumMaybeM $ \v o -> Just <$> f v o
519 accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
520 accumMaybe f = accumMaybeM $ \v o -> return $ f v o
521 accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
522 mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
523 mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
524 mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
525 mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
526 mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
527 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
528 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
530mapAccum_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
532 (_ :: Behavior t a, result) <- mapAccum f z e
535mapAccumMaybe_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
536mapAccumMaybe_ f z e = do
537 (_ :: Behavior t a, result) <- mapAccumMaybe f z e
540mapAccumM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
542 (_ :: Behavior t a, result) <- mapAccumM f z e
545mapAccumMaybeM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
546mapAccumMaybeM_ f z e = do
547 (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
550instance Reflex t => Accumulator t (Dynamic t) where
551 accumMaybeM f z e = do
552 rec let e' = flip push e $ \o -> do
553 v <- sample $ current d'
557 mapAccumMaybeM f z e = do
558 rec let e' = flip push e $ \o -> do
559 v <- sample $ current d'
561 return $ case result of
562 (Nothing, Nothing) -> Nothing
564 d' <- holdDyn z $ fmapMaybe fst e'
565 return (d', fmapMaybe snd e')
567instance Reflex t => Accumulator t (Behavior t) where
568 accumMaybeM f z e = current <$> accumMaybeM f z e
569 mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
571instance Reflex t => Accumulator t (Event t) where
572 accumMaybeM f z e = updated <$> accumMaybeM f z e
573 mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
575-- | Create a new 'Event' by combining each occurence with the next value
576-- of the list using the supplied function. If the list runs out of items,
577-- all subsequent 'Event' occurrences will be ignored.
578zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
579zipListWithEvent f l e = do
580 let f' a b = case a of
581 h:t -> (Just t, Just $ f h b)
582 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
583 mapAccumMaybe_ f' l e
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list