on ba77fac help sign in
patch/src/Reflex/Dynamic.hs
575 lines 26 kB history raw
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE NoMonomorphismRestriction #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE UndecidableInstances #-}
15#ifdef USE_REFLEX_OPTIMIZER
16{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
17#endif
18-- | This module contains various functions for working with 'Dynamic' values.
19-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
20module Reflex.Dynamic
21 ( -- * Basics
22 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
23 , current
24 , updated
25 , holdDyn
26 , constDyn
27 , uniqDyn
28 , uniqDynBy
29 , count
30 , toggle
31 , switchPromptlyDyn
32 , tagPromptlyDyn
33 , attachPromptlyDyn
34 , attachPromptlyDynWith
35 , attachPromptlyDynWithMaybe
36 , foldDyn
37 , foldDynM
38 , foldDynMaybe
39 , foldDynMaybeM
40 , joinDynThroughMap
41 , traceDyn
42 , traceDynWith
43 , splitDynPure
44 , distributeMapOverDynPure
45 , distributeDMapOverDynPure
46 , distributeListOverDynPure
47 , Demux
48 , demux
49 , demuxed
50 -- Things that probably aren't very useful:
51 , HList (..)
52 , FHList (..)
53 , collectDynPure
54 , distributeFHListOverDynPure
55 -- Unsafe
56 , unsafeDynamic
57 -- * Deprecated functions
58 , apDyn
59 , attachDyn
60 , attachDynWith
61 , attachDynWithMaybe
62 , collectDyn
63 , combineDyn
64 , distributeDMapOverDyn
65 , distributeFHListOverDyn
66 , forDyn
67 , forDynM
68 , getDemuxed
69 , joinDyn
70 , mapDyn
71 , mapDynM
72 , mconcatDyn
73 , nubDyn
74 , splitDyn
75 , tagDyn
76 ) where
78import Prelude hiding (mapM, mapM_)
80import Data.Functor.Misc
81import Reflex.Class
83import Control.Applicative ((<*>))
84import Control.Monad hiding (forM, forM_, mapM, mapM_)
85import Control.Monad.Fix
86import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
87import Data.Align
88import Data.Dependent.Map (DMap)
89import qualified Data.Dependent.Map as DMap
90import Data.Dependent.Sum (DSum (..))
91import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
92import Data.Map (Map)
93import Data.Monoid
94import Data.These
96import Debug.Trace
98-- | Create a new 'Dynamic' that only signals changes if the values actually
99-- changed.
100uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
101uniqDyn = uniqDynBy (==)
103-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
104-- changes and the given function returns 'False' when given both the old and
105-- the new values.
106uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
107uniqDynBy eq d =
108 let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
109 in unsafeDynamic (current d) e'
111-- | Create a 'Dynamic' using the initial value and change it each time the
112-- 'Event' occurs using a folding function on the previous value and the value
113-- of the 'Event'.
114foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
115foldDyn = accum . flip
117-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
118-- can 'sample' existing 'Behaviors' and 'hold' new ones.
119foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
120foldDynM = accumM . flip
122-- | Create a 'Dynamic' using the provided initial value and change it each time
123-- the provided 'Event' occurs, using a function to combine the old value with
124-- the 'Event''s value. If the function returns 'Nothing', the value is not
125-- changed; this is distinct from returning 'Just' the old value, since the
126-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
127-- in the 'Nothing' case.
128foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
129foldDynMaybe = accumMaybe . flip
131-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
132-- can 'sample' existing 'Behaviors' and 'hold' new ones.
133foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
134foldDynMaybeM = accumMaybeM . flip
136-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
137count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
138count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
140-- | Create a new 'Dynamic' using the initial value that flips its
141-- value every time the 'Event' occurs.
142toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
143toggle = foldDyn (const not)
145-- | Switches to the new 'Event' whenever it receives one. Switching occurs
146-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
147-- old and new inner Events fire simultaneously, the output will fire with the
148-- value of the *new* 'Event'.
149switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
150switchPromptlyDyn de =
151 let eLag = switch $ current de
152 eCoincidences = coincidence $ updated de
153 in leftmost [eCoincidences, eLag]
155-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
156splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
157splitDynPure d = (fmap fst d, fmap snd d)
159-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
160-- non-'Dynamic' elements.
161distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
162distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
164-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
165-- non-'Dynamic' elements, preserving the order of the elements.
166distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
167distributeListOverDynPure =
168 fmap (map fromDSum . DMap.toAscList) .
169 distributeDMapOverDynPure .
170 DMap.fromDistinctAscList .
171 zipWith toDSum [0..]
172 where
173 toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
174 toDSum k v = Const2 k :=> v
175 fromDSum :: DSum (Const2 Int a) Identity -> a
176 fromDSum (Const2 _ :=> Identity v) = v
178--TODO: Generalize this to functors other than Maps
179-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
180-- with the current values of the 'Dynamic's in a map.
181joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
182joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
184-- | Print the value of the 'Dynamic' on each change and prefix it
185-- with the provided string. This should /only/ be used for debugging.
186--
187-- Note: Just like Debug.Trace.trace, the value will only be shown if something
188-- else in the system is depending on it.
189traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
190traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
192-- | Print the result of applying the provided function to the value
193-- of the 'Dynamic' on each change. This should /only/ be used for
194-- debugging.
195--
196-- Note: Just like Debug.Trace.trace, the value will only be shown if something
197-- else in the system is depending on it.
198traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
199traceDynWith f d =
200 let e' = traceEventWith f $ updated d
201 getV0 = do
202 x <- sample $ current d
203 trace (f x) $ return x
204 in unsafeBuildDynamic getV0 e'
206-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
207-- each time the 'Event' occurs.
208--
209-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
210-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
211-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
212-- will be used, since the 'Behavior' won't be updated until the end of the frame.
213-- Additionally, this means that the output 'Event' may not be used to directly change
214-- the input 'Dynamic', because that would mean its value depends on itself. When creating
215-- cyclic data flows, generally `tag (current d) e` is preferred.
216tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
217tagPromptlyDyn = attachPromptlyDynWith const
219-- | Attach the current value of the 'Dynamic' to the value of the
220-- 'Event' each time it occurs.
221--
222-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
223attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
224attachPromptlyDyn = attachPromptlyDynWith (,)
226-- | Combine the current value of the 'Dynamic' with the value of the
227-- 'Event' each time it occurs.
228--
229-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
230attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
231attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
233-- | Create a new 'Event' by combining the value at each occurence with the
234-- current value of the 'Dynamic' value and possibly filtering if the combining
235-- function returns 'Nothing'.
236--
237-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
238-- (current d)`. See 'tagPromptlyDyn' for details.
239attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
240attachPromptlyDynWithMaybe f d e =
241 let e' = attach (current d) e
242 in fforMaybe (align e' $ updated d) $ \case
243 This (a, b) -> f a b -- Only the tagging event is firing, so use that
244 These (_, b) a -> f a b -- Both events are firing, so use the newer value
245 That _ -> Nothing -- The tagging event isn't firing, so don't fire
247--------------------------------------------------------------------------------
248-- Demux
249--------------------------------------------------------------------------------
251-- | Represents a time changing value together with an 'EventSelector' that can
252-- efficiently detect when the underlying 'Dynamic' has a particular value.
253-- This is useful for representing data like the current selection of a long
254-- list.
255--
256-- Semantically,
257--
258-- > demuxed (demux d) k === fmap (== k) d
259--
260-- However, the when getDemuxed is used multiple times, the complexity is only
261-- /O(log(n))/, rather than /O(n)/ for fmap.
262data Demux t k = Demux { demuxValue :: Behavior t k
263 , demuxSelector :: EventSelector t (Const2 k Bool)
264 }
266-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
267-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
268demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
269demux k = Demux (current k)
270 (fan $ attachWith (\k0 k1 -> if k0 == k1
271 then DMap.empty
272 else DMap.fromList [Const2 k0 :=> Identity False,
273 Const2 k1 :=> Identity True])
274 (current k) (updated k))
276-- | Select a particular output of the 'Demux'; this is equivalent to (but much
277-- faster than) mapping over the original 'Dynamic' and checking whether it is
278-- equal to the given key.
279demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
280demuxed d k =
281 let e = select (demuxSelector d) (Const2 k)
282 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
284--------------------------------------------------------------------------------
285-- collectDyn
286--------------------------------------------------------------------------------
288--TODO: This whole section is badly in need of cleanup
290-- | A heterogeneous list whose type and length are fixed statically. This is
291-- reproduced from the 'HList' package due to integration issues, and because
292-- very little other functionality from that library is needed.
293data HList (l::[*]) where
294 HNil :: HList '[]
295 HCons :: e -> HList l -> HList (e ': l)
297infixr 2 `HCons`
299type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
300type instance HRevApp '[] l = l
301type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
303hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
304hRevApp HNil l = l
305hRevApp (HCons x l) l' = hRevApp l (HCons x l')
307hReverse :: HList l -> HList (HRevApp l '[])
308hReverse l = hRevApp l HNil
310hBuild :: (HBuild' '[] r) => r
311hBuild = hBuild' HNil
313class HBuild' l r where
314 hBuild' :: HList l -> r
316instance (l' ~ HRevApp l '[])
317 => HBuild' l (HList l') where
318 hBuild' = hReverse
320instance HBuild' (a ': l) r
321 => HBuild' l (a->r) where
322 hBuild' l x = hBuild' (HCons x l)
324-- | Like 'HList', but with a functor wrapping each element.
325data FHList f l where
326 FHNil :: FHList f '[]
327 FHCons :: f e -> FHList f l -> FHList f (e ': l)
329instance GEq (HListPtr l) where
330 HHeadPtr `geq` HHeadPtr = Just Refl
331 HHeadPtr `geq` HTailPtr _ = Nothing
332 HTailPtr _ `geq` HHeadPtr = Nothing
333 HTailPtr a `geq` HTailPtr b = a `geq` b
335instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
336 HHeadPtr `gcompare` HHeadPtr = GEQ
337 HHeadPtr `gcompare` HTailPtr _ = GLT
338 HTailPtr _ `gcompare` HHeadPtr = GGT
339 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
341data HListPtr l a where
342 HHeadPtr :: HListPtr (h ': t) h
343 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
345fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
346fhlistToDMap = DMap.fromList . go
347 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
348 go = \case
349 FHNil -> []
350 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
352class RebuildSortedHList l where
353 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
354 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
356instance RebuildSortedHList '[] where
357 rebuildSortedFHList l = case l of
358 [] -> FHNil
359 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
360 rebuildSortedHList l = case l of
361 [] -> HNil
362 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
364instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
365 rebuildSortedFHList l = case l of
366 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
367 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
368 rebuildSortedHList l = case l of
369 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
370 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
372dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
373dmapToHList = rebuildSortedHList . DMap.toList
375-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
376-- single 'Dynamic' whose value represents the current values of all of the
377-- input 'Dynamic's.
378distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
379distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
381class AllAreFunctors (f :: a -> *) (l :: [a]) where
382 type FunctorList f l :: [*]
383 toFHList :: HList (FunctorList f l) -> FHList f l
384 fromFHList :: FHList f l -> HList (FunctorList f l)
386instance AllAreFunctors f '[] where
387 type FunctorList f '[] = '[]
388 toFHList l = case l of
389 HNil -> FHNil
390#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
391 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
392#endif
393 fromFHList FHNil = HNil
395instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
396 type FunctorList f (h ': t) = f h ': FunctorList f t
397 toFHList l = case l of
398 a `HCons` b -> a `FHCons` toFHList b
399#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
400 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
401#endif
402 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
404-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
405-- single 'Dynamic' whose value represents all the current values of the input's
406-- consitutent 'Dynamic's.
407collectDynPure :: ( RebuildSortedHList (HListElems b)
408 , IsHList a, IsHList b
409 , AllAreFunctors (Dynamic t) (HListElems b)
410 , Reflex t
411 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
412 ) => a -> Dynamic t b
413collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
415-- Poor man's Generic
416class IsHList a where
417 type HListElems a :: [*]
418 toHList :: a -> HList (HListElems a)
419 fromHList :: HList (HListElems a) -> a
421instance IsHList (a, b) where
422 type HListElems (a, b) = [a, b]
423 toHList (a, b) = hBuild a b
424 fromHList l = case l of
425 a `HCons` b `HCons` HNil -> (a, b)
426#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
427 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
428#endif
430instance IsHList (a, b, c, d) where
431 type HListElems (a, b, c, d) = [a, b, c, d]
432 toHList (a, b, c, d) = hBuild a b c d
433 fromHList l = case l of
434 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
435#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
436 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
437#endif
439instance IsHList (a, b, c, d, e, f) where
440 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
441 toHList (a, b, c, d, e, f) = hBuild a b c d e f
442 fromHList l = case l of
443 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
444#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
445 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
446#endif
448--------------------------------------------------------------------------------
449-- Deprecated functions
450--------------------------------------------------------------------------------
452-- | Map a function over a 'Dynamic'.
453{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
454mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
455mapDyn f = return . fmap f
457-- | 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.
458{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
459mapDynM :: 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)
460mapDynM f d = do
461 let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
462 eb' = fmap constant e'
463 v0 = pull $ f =<< sample (current d)
464 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
465 let b' = pull $ sample =<< sample bb'
466 return $ unsafeDynamic b' e'
468-- | Flipped version of 'mapDyn'.
469{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
470forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
471forDyn a = return . ffor a
473-- | Flipped version of 'mapDynM'
474{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
475forDynM :: 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)
476forDynM d f = mapDynM f d
478-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
479-- of the tuple.
480{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
481splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
482splitDyn = return . splitDynPure
484-- | Merge the 'Dynamic' values using their 'Monoid' instance.
485{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
486mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
487mconcatDyn = return . mconcat
489-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
490{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
491distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
492distributeDMapOverDyn = return . distributeDMapOverDynPure
494-- | Merge two 'Dynamic's into a new one using the provided function. The new
495-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
496-- its value.
497{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
498combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
499combineDyn f a b = return $ zipDynWith f a b
501-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
502--
503-- > do
504-- > person <- Person `mapDyn` dynFirstName
505-- > `apDyn` dynListName
506-- > `apDyn` dynAge
507-- > `apDyn` dynAddress
508{-# 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" #-}
509{-# ANN apDyn "HLint: ignore Use fmap" #-}
510apDyn :: forall t m a b. (Reflex t, Monad m)
511 => m (Dynamic t (a -> b))
512 -> Dynamic t a
513 -> m (Dynamic t b)
514apDyn m a = liftM (<*> a) m
516--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
517-- | This function no longer needs to be monadic, so it has been replaced by
518-- 'demuxed', which is pure.
519{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
520getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
521getDemuxed d = return . demuxed d
523-- | This function no longer needs to be monadic, so it has been replaced by
524-- 'distributeFHListOverDynPure', which is pure.
525{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
526distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
527distributeFHListOverDyn = return . distributeFHListOverDynPure
529-- | This function no longer needs to be monadic, so it has been replaced by
530-- 'collectDynPure', which is pure.
531{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
532collectDyn :: ( RebuildSortedHList (HListElems b)
533 , IsHList a, IsHList b
534 , AllAreFunctors (Dynamic t) (HListElems b)
535 , Reflex t, Monad m
536 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
537 ) => a -> m (Dynamic t b)
538collectDyn = return . collectDynPure
540-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
541-- semantics.
542{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
543tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
544tagDyn = tagPromptlyDyn
546-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
547-- semantics.
548{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
549attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
550attachDyn = attachPromptlyDyn
552-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
553-- semantics.
554{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
555attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
556attachDynWith = attachPromptlyDynWith
558-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
559-- its semantics.
560{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
561attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
562attachDynWithMaybe = attachPromptlyDynWithMaybe
564-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
565-- current value will always be equal to the current value's current value, and
566-- will change whenever either the inner or the outer (or both) values change.
567{-# DEPRECATED joinDyn "Use 'join' instead" #-}
568joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
569joinDyn = join
571-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
572-- been renamed to 'uniqDyn'
573{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
574nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
575nubDyn = uniqDyn