2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE NoMonomorphismRestriction #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE RecursiveDo #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE StandaloneDeriving #-}
14{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeOperators #-}
16{-# LANGUAGE UndecidableInstances #-}
17#ifdef USE_REFLEX_OPTIMIZER
18{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20-- | This module contains various functions for working with 'Dynamic' values.
21-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
24 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
34 , attachPromptlyDynWith
35 , attachPromptlyDynWithMaybe
50 , distributeMapOverDynPure
51 , distributeDMapOverDynPure
52 , distributeListOverDynPure
56 -- Things that probably aren't very useful:
60 , RebuildSortedHList (..)
64 , distributeFHListOverDynPure
67 -- * Deprecated functions
74 , distributeDMapOverDyn
75 , distributeFHListOverDyn
90import Data.Functor.Misc
93import Control.Applicative ((<*>))
95import Control.Monad.Fix
96import Control.Monad.Identity
98import Data.Dependent.Map (DMap)
99import qualified Data.Dependent.Map as DMap
100import Data.Dependent.Sum (DSum (..))
101import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
109-- | Create a new 'Dynamic' that only signals changes if the values actually
111holdUniqDyn :: (Reflex t, MonadHold t m, MonadFix m, Eq a) => Dynamic t a -> m (Dynamic t a)
112holdUniqDyn = holdUniqDynBy (==)
114-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
115-- changes and the given function returns 'False' when given both the old and
117holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
118holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
120-- | Dynamic Maybe that can only update from Nothing to Just or Just to Just (i.e., cannot revert to Nothing)
121improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
122improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
124-- | Create a 'Dynamic' that accumulates values from another 'Dynamic'. This
125-- function does not force its input 'Dynamic' until the output 'Dynamic' is
127scanDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> b) -> Dynamic t a -> m (Dynamic t b)
128scanDyn z f = scanDynMaybe z (\a b -> Just $ f a b)
130-- | Like 'scanDyn', but the the accumulator function may decline to update the
131-- result 'Dynamic''s value.
132scanDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> Maybe b) -> Dynamic t a -> m (Dynamic t b)
133scanDynMaybe z f d = do
134 rec d' <- buildDynamic (z <$> sample (current d)) $ flip push (updated d) $ \a -> do
135 b <- sample $ current d'
139-- | Create a 'Dynamic' using the initial value and change it each time the
140-- 'Event' occurs using a folding function on the previous value and the value
142foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
143foldDyn = accum . flip
145-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
146-- can 'sample' existing 'Behaviors' and 'hold' new ones.
147foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
148foldDynM = accumM . flip
150-- | Create a 'Dynamic' using the provided initial value and change it each time
151-- the provided 'Event' occurs, using a function to combine the old value with
152-- the 'Event''s value. If the function returns 'Nothing', the value is not
153-- changed; this is distinct from returning 'Just' the old value, since the
154-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
155-- in the 'Nothing' case.
156foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
157foldDynMaybe = accumMaybe . flip
159-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
160-- can 'sample' existing 'Behaviors' and 'hold' new ones.
161foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
162foldDynMaybeM = accumMaybeM . flip
164-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
165count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
166count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
168-- | Create a new 'Dynamic' using the initial value that flips its
169-- value every time the 'Event' occurs.
170toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
171toggle = foldDyn (const not)
173-- | Switches to the new 'Event' whenever it receives one. Switching occurs
174-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
175-- old and new inner Events fire simultaneously, the output will fire with the
176-- value of the *new* 'Event'.
177switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
178switchPromptlyDyn de =
179 let eLag = switch $ current de
180 eCoincidences = coincidence $ updated de
181 in leftmost [eCoincidences, eLag]
183-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
184splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
185splitDynPure d = (fmap fst d, fmap snd d)
187-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
188-- non-'Dynamic' elements.
189distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
190distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
192-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
193-- non-'Dynamic' elements, preserving the order of the elements.
194distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
195distributeListOverDynPure =
196 fmap (map fromDSum . DMap.toAscList) .
197 distributeDMapOverDynPure .
198 DMap.fromDistinctAscList .
201 toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
202 toDSum k v = Const2 k :=> v
203 fromDSum :: DSum (Const2 Int a) Identity -> a
204 fromDSum (Const2 _ :=> Identity v) = v
206--TODO: Generalize this to functors other than Maps
207-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
208-- with the current values of the 'Dynamic's in a map.
209joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
210joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
212-- | Print the value of the 'Dynamic' when it is first read and on each
213-- subsequent change that is observed (as traceEvent), prefixed with the
214-- provided string. This should /only/ be used for debugging.
216-- Note: Just like Debug.Trace.trace, the value will only be shown if something
217-- else in the system is depending on it.
218traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
219traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
221-- | Print the result of applying the provided function to the value
222-- of the 'Dynamic' when it is first read and on each subsequent change
223-- that is observed (as traceEvent). This should /only/ be used for
226-- Note: Just like Debug.Trace.trace, the value will only be shown if something
227-- else in the system is depending on it.
228traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
230 let e' = traceEventWith f $ updated d
232 x <- sample $ current d
233 trace (f x) $ return x
234 in unsafeBuildDynamic getV0 e'
236-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
237-- each time the 'Event' occurs.
239-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
240-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
241-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
242-- will be used, since the 'Behavior' won't be updated until the end of the frame.
243-- Additionally, this means that the output 'Event' may not be used to directly change
244-- the input 'Dynamic', because that would mean its value depends on itself. When creating
245-- cyclic data flows, generally `tag (current d) e` is preferred.
246tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
247tagPromptlyDyn = attachPromptlyDynWith const
249-- | Attach the current value of the 'Dynamic' to the value of the
250-- 'Event' each time it occurs.
252-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
253attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
254attachPromptlyDyn = attachPromptlyDynWith (,)
256-- | Combine the current value of the 'Dynamic' with the value of the
257-- 'Event' each time it occurs.
259-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
260attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
261attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
263-- | Create a new 'Event' by combining the value at each occurence with the
264-- current value of the 'Dynamic' value and possibly filtering if the combining
265-- function returns 'Nothing'.
267-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
268-- (current d)`. See 'tagPromptlyDyn' for details.
269attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
270attachPromptlyDynWithMaybe f d e =
271 let e' = attach (current d) e
272 in fforMaybe (align e' $ updated d) $ \case
273 This (a, b) -> f a b -- Only the tagging event is firing, so use that
274 These (_, b) a -> f a b -- Both events are firing, so use the newer value
275 That _ -> Nothing -- The tagging event isn't firing, so don't fire
277-- | Factor a @Dynamic t (Maybe a)@ into a @Dynamic t (Maybe (Dynamic t a))@,
278-- such that the outer 'Dynamic' is updated only when the 'Maybe''s constructor
279-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
280-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
281-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
282-- existing inner 'Dynamic' will become constant, and will not change when the
283-- outer constructor changes back to 'Nothing'.
284maybeDyn :: forall t a m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe (Dynamic t a)))
286 ma <- sample $ current d
287 let inner :: forall m'. (MonadFix m', MonadHold t m') => a -> m' (Dynamic t a)
288 inner a = holdDyn a . fmapMaybe id =<< takeWhileE isJust (updated d)
289 mInner0 :: Maybe (Dynamic t a) <- mapM inner ma
290 rec result <- holdDyn mInner0 $ flip push (updated d) $ \new -> do
291 old <- sample $ current d
292 if isJust old == isJust new then return Nothing else Just <$> mapM inner new
295--------------------------------------------------------------------------------
297--------------------------------------------------------------------------------
299-- | Represents a time changing value together with an 'EventSelector' that can
300-- efficiently detect when the underlying 'Dynamic' has a particular value.
301-- This is useful for representing data like the current selection of a long
306-- > demuxed (demux d) k === fmap (== k) d
308-- However, the when getDemuxed is used multiple times, the complexity is only
309-- /O(log(n))/, rather than /O(n)/ for fmap.
310data Demux t k = Demux { demuxValue :: Behavior t k
311 , demuxSelector :: EventSelector t (Const2 k Bool)
314-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
315-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
316demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
317demux k = Demux (current k)
318 (fan $ attachWith (\k0 k1 -> if k0 == k1
320 else DMap.fromList [Const2 k0 :=> Identity False,
321 Const2 k1 :=> Identity True])
322 (current k) (updated k))
324-- | Select a particular output of the 'Demux'; this is equivalent to (but much
325-- faster than) mapping over the original 'Dynamic' and checking whether it is
326-- equal to the given key.
327demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
329 let e = select (demuxSelector d) (Const2 k)
330 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
332--------------------------------------------------------------------------------
334--------------------------------------------------------------------------------
336--TODO: This whole section is badly in need of cleanup
338-- | A heterogeneous list whose type and length are fixed statically. This is
339-- reproduced from the 'HList' package due to integration issues, and because
340-- very little other functionality from that library is needed.
341data HList (l::[*]) where
343 HCons :: e -> HList l -> HList (e ': l)
347type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
348type instance HRevApp '[] l = l
349type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
351hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
353hRevApp (HCons x l) l' = hRevApp l (HCons x l')
355hReverse :: HList l -> HList (HRevApp l '[])
356hReverse l = hRevApp l HNil
358hBuild :: (HBuild' '[] r) => r
361class HBuild' l r where
362 hBuild' :: HList l -> r
364instance (l' ~ HRevApp l '[])
365 => HBuild' l (HList l') where
368instance HBuild' (a ': l) r
369 => HBuild' l (a->r) where
370 hBuild' l x = hBuild' (HCons x l)
372-- | Like 'HList', but with a functor wrapping each element.
374 FHNil :: FHList f '[]
375 FHCons :: f e -> FHList f l -> FHList f (e ': l)
377instance GEq (HListPtr l) where
378#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
380 geqUnify HHeadPtr HHeadPtr _ same = same
381 geqUnify (HTailPtr a) (HTailPtr b) different same = geqUnify a b different same
382 geqUnify _ _ different _ = different
384 HHeadPtr `geq` HHeadPtr = Just Refl
385 HHeadPtr `geq` HTailPtr _ = Nothing
386 HTailPtr _ `geq` HHeadPtr = Nothing
387 HTailPtr a `geq` HTailPtr b = a `geq` b
389instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
390#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
393 HHeadPtr `gcompare` HHeadPtr = GEQ
394 HHeadPtr `gcompare` HTailPtr _ = GLT
395 HTailPtr _ `gcompare` HHeadPtr = GGT
396 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
398-- | A typed index into a typed heterogeneous list.
399data HListPtr l a where
400 HHeadPtr :: HListPtr (h ': t) h
401 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
403deriving instance Eq (HListPtr l a)
404deriving instance Ord (HListPtr l a)
406fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
407fhlistToDMap = DMap.fromList . go
408 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
411 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
413-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
414-- they must be contiguous and sorted.
415class RebuildSortedHList l where
416 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
417 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
419instance RebuildSortedHList '[] where
420 rebuildSortedFHList l = case l of
422 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
423 rebuildSortedHList l = case l of
425 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
427instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
428 rebuildSortedFHList l = case l of
429 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
430 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
431 rebuildSortedHList l = case l of
432 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
433 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
435dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
436dmapToHList = rebuildSortedHList . DMap.toList
438-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
439-- single 'Dynamic' whose value represents the current values of all of the
441distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
442distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
444-- | Indicates that all elements in a type-level list are applications of the
446class AllAreFunctors (f :: a -> *) (l :: [a]) where
447 type FunctorList f l :: [*]
448 toFHList :: HList (FunctorList f l) -> FHList f l
449 fromFHList :: FHList f l -> HList (FunctorList f l)
451instance AllAreFunctors f '[] where
452 type FunctorList f '[] = '[]
453 toFHList l = case l of
455#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
456 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
458 fromFHList FHNil = HNil
460instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
461 type FunctorList f (h ': t) = f h ': FunctorList f t
462 toFHList l = case l of
463 a `HCons` b -> a `FHCons` toFHList b
464#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
465 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
467 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
469-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
470-- single 'Dynamic' whose value represents all the current values of the input's
471-- consitutent 'Dynamic's.
472collectDynPure :: ( RebuildSortedHList (HListElems b)
473 , IsHList a, IsHList b
474 , AllAreFunctors (Dynamic t) (HListElems b)
476 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
477 ) => a -> Dynamic t b
478collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
480-- | Poor man's 'Generic's for product types only.
482 type HListElems a :: [*]
483 toHList :: a -> HList (HListElems a)
484 fromHList :: HList (HListElems a) -> a
486instance IsHList (a, b) where
487 type HListElems (a, b) = [a, b]
488 toHList (a, b) = hBuild a b
489 fromHList l = case l of
490 a `HCons` b `HCons` HNil -> (a, b)
491#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
492 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
495instance IsHList (a, b, c, d) where
496 type HListElems (a, b, c, d) = [a, b, c, d]
497 toHList (a, b, c, d) = hBuild a b c d
498 fromHList l = case l of
499 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
500#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
501 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
504instance IsHList (a, b, c, d, e, f) where
505 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
506 toHList (a, b, c, d, e, f) = hBuild a b c d e f
507 fromHList l = case l of
508 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
509#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
510 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
513--------------------------------------------------------------------------------
514-- Deprecated functions
515--------------------------------------------------------------------------------
517-- | Map a function over a 'Dynamic'.
518{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
519mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
520mapDyn f = return . fmap f
522-- | Map a sampling function over a 'Dynamic'. The sampling function will use the input 'Dynamic''s 'current' 'Behavior' until the first time its 'updated' 'Event' fires, at which point it will be invoked only once each time the 'Event' fires.
523{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
524mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b)
526 let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
527 eb' = fmap constant e'
528 v0 = pull $ f =<< sample (current d)
529 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
530 let b' = pull $ sample =<< sample bb'
531 return $ unsafeDynamic b' e'
533-- | Flipped version of 'mapDyn'.
534{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
535forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
536forDyn a = return . ffor a
538-- | Flipped version of 'mapDynM'
539{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
540forDynM :: forall t m a b. (Reflex t, MonadHold t m) => Dynamic t a -> (forall m'. MonadSample t m' => a -> m' b) -> m (Dynamic t b)
541forDynM d f = mapDynM f d
543-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
545{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
546splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
547splitDyn = return . splitDynPure
549-- | Merge the 'Dynamic' values using their 'Monoid' instance.
550{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
551mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
552mconcatDyn = return . mconcat
554-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
555{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
556distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
557distributeDMapOverDyn = return . distributeDMapOverDynPure
559-- | Merge two 'Dynamic's into a new one using the provided function. The new
560-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
562{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
563combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
564combineDyn f a b = return $ zipDynWith f a b
566-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
569-- > person <- Person `mapDyn` dynFirstName
570-- > `apDyn` dynListName
572-- > `apDyn` dynAddress
573{-# DEPRECATED apDyn "Use 'ffor m (<*> a)' instead of 'apDyn m a'; consider eliminating monadic style, since Dynamics are now Applicative and can be used with applicative style directly" #-}
574#ifdef USE_TEMPLATE_HASKELL
575{-# ANN apDyn "HLint: ignore Use fmap" #-}
577apDyn :: forall t m a b. (Reflex t, Monad m)
578 => m (Dynamic t (a -> b))
581apDyn m a = fmap (<*> a) m
583--TODO: The pattern of using hold (sample b0) can be reused in various places as a safe way of building certain kinds of Dynamics; see if we can factor this out
584-- | This function no longer needs to be monadic, so it has been replaced by
585-- 'demuxed', which is pure.
586{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
587getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
588getDemuxed d = return . demuxed d
590-- | This function no longer needs to be monadic, so it has been replaced by
591-- 'distributeFHListOverDynPure', which is pure.
592{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
593distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
594distributeFHListOverDyn = return . distributeFHListOverDynPure
596-- | This function no longer needs to be monadic, so it has been replaced by
597-- 'collectDynPure', which is pure.
598{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
599collectDyn :: ( RebuildSortedHList (HListElems b)
600 , IsHList a, IsHList b
601 , AllAreFunctors (Dynamic t) (HListElems b)
603 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
604 ) => a -> m (Dynamic t b)
605collectDyn = return . collectDynPure
607-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
609{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
610tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
611tagDyn = tagPromptlyDyn
613-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
615{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
616attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
617attachDyn = attachPromptlyDyn
619-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
621{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
622attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
623attachDynWith = attachPromptlyDynWith
625-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
627{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
628attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
629attachDynWithMaybe = attachPromptlyDynWithMaybe
631-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
632-- current value will always be equal to the current value's current value, and
633-- will change whenever either the inner or the outer (or both) values change.
634{-# DEPRECATED joinDyn "Use 'join' instead" #-}
635joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
638-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
639-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
640-- circumstances. Also, note that, unlike 'nub', this function does not prevent
641-- all recurrences of a value, only consecutive recurrences.
642{-# DEPRECATED nubDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
643nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
646-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
647-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
649{-# DEPRECATED uniqDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
650uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
651uniqDyn = uniqDynBy (==)
653-- | WARNING: This function is impure. Use 'holdUniqDynBy' instead.
654{-# DEPRECATED uniqDynBy "Use 'holdUniqDynBy' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
655uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
657 let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
658 in unsafeDynamic (current d) 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