on daa1571 help sign in
patch/src/Reflex/Dynamic.hs
584 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 , RebuildSortedHList (..)
55 , IsHList (..)
56 , AllAreFunctors (..)
57 , HListPtr (..)
58 , distributeFHListOverDynPure
59 -- Unsafe
60 , unsafeDynamic
61 -- * Deprecated functions
62 , apDyn
63 , attachDyn
64 , attachDynWith
65 , attachDynWithMaybe
66 , collectDyn
67 , combineDyn
68 , distributeDMapOverDyn
69 , distributeFHListOverDyn
70 , forDyn
71 , forDynM
72 , getDemuxed
73 , joinDyn
74 , mapDyn
75 , mapDynM
76 , mconcatDyn
77 , nubDyn
78 , splitDyn
79 , tagDyn
80 ) where
82import Prelude hiding (mapM, mapM_)
84import Data.Functor.Misc
85import Reflex.Class
87import Control.Applicative ((<*>))
88import Control.Monad hiding (forM, forM_, mapM, mapM_)
89import Control.Monad.Fix
90import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
91import Data.Align
92import Data.Dependent.Map (DMap)
93import qualified Data.Dependent.Map as DMap
94import Data.Dependent.Sum (DSum (..))
95import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
96import Data.Map (Map)
97import Data.Monoid
98import Data.These
100import Debug.Trace
102-- | Create a new 'Dynamic' that only signals changes if the values actually
103-- changed.
104uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
105uniqDyn = uniqDynBy (==)
107-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
108-- changes and the given function returns 'False' when given both the old and
109-- the new values.
110uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
111uniqDynBy eq d =
112 let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
113 in unsafeDynamic (current d) e'
115-- | Create a 'Dynamic' using the initial value and change it each time the
116-- 'Event' occurs using a folding function on the previous value and the value
117-- of the 'Event'.
118foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
119foldDyn = accum . flip
121-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
122-- can 'sample' existing 'Behaviors' and 'hold' new ones.
123foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
124foldDynM = accumM . flip
126-- | Create a 'Dynamic' using the provided initial value and change it each time
127-- the provided 'Event' occurs, using a function to combine the old value with
128-- the 'Event''s value. If the function returns 'Nothing', the value is not
129-- changed; this is distinct from returning 'Just' the old value, since the
130-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
131-- in the 'Nothing' case.
132foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
133foldDynMaybe = accumMaybe . flip
135-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
136-- can 'sample' existing 'Behaviors' and 'hold' new ones.
137foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
138foldDynMaybeM = accumMaybeM . flip
140-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
141count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
142count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
144-- | Create a new 'Dynamic' using the initial value that flips its
145-- value every time the 'Event' occurs.
146toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
147toggle = foldDyn (const not)
149-- | Switches to the new 'Event' whenever it receives one. Switching occurs
150-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
151-- old and new inner Events fire simultaneously, the output will fire with the
152-- value of the *new* 'Event'.
153switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
154switchPromptlyDyn de =
155 let eLag = switch $ current de
156 eCoincidences = coincidence $ updated de
157 in leftmost [eCoincidences, eLag]
159-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
160splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
161splitDynPure d = (fmap fst d, fmap snd d)
163-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
164-- non-'Dynamic' elements.
165distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
166distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
168-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
169-- non-'Dynamic' elements, preserving the order of the elements.
170distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
171distributeListOverDynPure =
172 fmap (map fromDSum . DMap.toAscList) .
173 distributeDMapOverDynPure .
174 DMap.fromDistinctAscList .
175 zipWith toDSum [0..]
176 where
177 toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
178 toDSum k v = Const2 k :=> v
179 fromDSum :: DSum (Const2 Int a) Identity -> a
180 fromDSum (Const2 _ :=> Identity v) = v
182--TODO: Generalize this to functors other than Maps
183-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
184-- with the current values of the 'Dynamic's in a map.
185joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
186joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
188-- | Print the value of the 'Dynamic' on each change and prefix it
189-- with the provided string. This should /only/ be used for debugging.
190--
191-- Note: Just like Debug.Trace.trace, the value will only be shown if something
192-- else in the system is depending on it.
193traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
194traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
196-- | Print the result of applying the provided function to the value
197-- of the 'Dynamic' on each change. This should /only/ be used for
198-- debugging.
199--
200-- Note: Just like Debug.Trace.trace, the value will only be shown if something
201-- else in the system is depending on it.
202traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
203traceDynWith f d =
204 let e' = traceEventWith f $ updated d
205 getV0 = do
206 x <- sample $ current d
207 trace (f x) $ return x
208 in unsafeBuildDynamic getV0 e'
210-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
211-- each time the 'Event' occurs.
212--
213-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
214-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
215-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
216-- will be used, since the 'Behavior' won't be updated until the end of the frame.
217-- Additionally, this means that the output 'Event' may not be used to directly change
218-- the input 'Dynamic', because that would mean its value depends on itself. When creating
219-- cyclic data flows, generally `tag (current d) e` is preferred.
220tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
221tagPromptlyDyn = attachPromptlyDynWith const
223-- | Attach the current value of the 'Dynamic' to the value of the
224-- 'Event' each time it occurs.
225--
226-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
227attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
228attachPromptlyDyn = attachPromptlyDynWith (,)
230-- | Combine the current value of the 'Dynamic' with the value of the
231-- 'Event' each time it occurs.
232--
233-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
234attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
235attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
237-- | Create a new 'Event' by combining the value at each occurence with the
238-- current value of the 'Dynamic' value and possibly filtering if the combining
239-- function returns 'Nothing'.
240--
241-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
242-- (current d)`. See 'tagPromptlyDyn' for details.
243attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
244attachPromptlyDynWithMaybe f d e =
245 let e' = attach (current d) e
246 in fforMaybe (align e' $ updated d) $ \case
247 This (a, b) -> f a b -- Only the tagging event is firing, so use that
248 These (_, b) a -> f a b -- Both events are firing, so use the newer value
249 That _ -> Nothing -- The tagging event isn't firing, so don't fire
251--------------------------------------------------------------------------------
252-- Demux
253--------------------------------------------------------------------------------
255-- | Represents a time changing value together with an 'EventSelector' that can
256-- efficiently detect when the underlying 'Dynamic' has a particular value.
257-- This is useful for representing data like the current selection of a long
258-- list.
259--
260-- Semantically,
261--
262-- > demuxed (demux d) k === fmap (== k) d
263--
264-- However, the when getDemuxed is used multiple times, the complexity is only
265-- /O(log(n))/, rather than /O(n)/ for fmap.
266data Demux t k = Demux { demuxValue :: Behavior t k
267 , demuxSelector :: EventSelector t (Const2 k Bool)
268 }
270-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
271-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
272demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
273demux k = Demux (current k)
274 (fan $ attachWith (\k0 k1 -> if k0 == k1
275 then DMap.empty
276 else DMap.fromList [Const2 k0 :=> Identity False,
277 Const2 k1 :=> Identity True])
278 (current k) (updated k))
280-- | Select a particular output of the 'Demux'; this is equivalent to (but much
281-- faster than) mapping over the original 'Dynamic' and checking whether it is
282-- equal to the given key.
283demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
284demuxed d k =
285 let e = select (demuxSelector d) (Const2 k)
286 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
288--------------------------------------------------------------------------------
289-- collectDyn
290--------------------------------------------------------------------------------
292--TODO: This whole section is badly in need of cleanup
294-- | A heterogeneous list whose type and length are fixed statically. This is
295-- reproduced from the 'HList' package due to integration issues, and because
296-- very little other functionality from that library is needed.
297data HList (l::[*]) where
298 HNil :: HList '[]
299 HCons :: e -> HList l -> HList (e ': l)
301infixr 2 `HCons`
303type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
304type instance HRevApp '[] l = l
305type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
307hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
308hRevApp HNil l = l
309hRevApp (HCons x l) l' = hRevApp l (HCons x l')
311hReverse :: HList l -> HList (HRevApp l '[])
312hReverse l = hRevApp l HNil
314hBuild :: (HBuild' '[] r) => r
315hBuild = hBuild' HNil
317class HBuild' l r where
318 hBuild' :: HList l -> r
320instance (l' ~ HRevApp l '[])
321 => HBuild' l (HList l') where
322 hBuild' = hReverse
324instance HBuild' (a ': l) r
325 => HBuild' l (a->r) where
326 hBuild' l x = hBuild' (HCons x l)
328-- | Like 'HList', but with a functor wrapping each element.
329data FHList f l where
330 FHNil :: FHList f '[]
331 FHCons :: f e -> FHList f l -> FHList f (e ': l)
333instance GEq (HListPtr l) where
334 HHeadPtr `geq` HHeadPtr = Just Refl
335 HHeadPtr `geq` HTailPtr _ = Nothing
336 HTailPtr _ `geq` HHeadPtr = Nothing
337 HTailPtr a `geq` HTailPtr b = a `geq` b
339instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
340 HHeadPtr `gcompare` HHeadPtr = GEQ
341 HHeadPtr `gcompare` HTailPtr _ = GLT
342 HTailPtr _ `gcompare` HHeadPtr = GGT
343 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
345-- | A typed index into a typed heterogeneous list.
346data HListPtr l a where
347 HHeadPtr :: HListPtr (h ': t) h
348 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
350fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
351fhlistToDMap = DMap.fromList . go
352 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
353 go = \case
354 FHNil -> []
355 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
357-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
358-- they must be contiguous and sorted.
359class RebuildSortedHList l where
360 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
361 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
363instance RebuildSortedHList '[] where
364 rebuildSortedFHList l = case l of
365 [] -> FHNil
366 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
367 rebuildSortedHList l = case l of
368 [] -> HNil
369 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
371instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
372 rebuildSortedFHList l = case l of
373 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
374 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
375 rebuildSortedHList l = case l of
376 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
377 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
379dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
380dmapToHList = rebuildSortedHList . DMap.toList
382-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
383-- single 'Dynamic' whose value represents the current values of all of the
384-- input 'Dynamic's.
385distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
386distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
388-- | Indicates that all elements in a type-level list are applications of the
389-- same functor.
390class AllAreFunctors (f :: a -> *) (l :: [a]) where
391 type FunctorList f l :: [*]
392 toFHList :: HList (FunctorList f l) -> FHList f l
393 fromFHList :: FHList f l -> HList (FunctorList f l)
395instance AllAreFunctors f '[] where
396 type FunctorList f '[] = '[]
397 toFHList l = case l of
398 HNil -> FHNil
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 FHNil = HNil
404instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
405 type FunctorList f (h ': t) = f h ': FunctorList f t
406 toFHList l = case l of
407 a `HCons` b -> a `FHCons` toFHList b
408#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
409 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
410#endif
411 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
413-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
414-- single 'Dynamic' whose value represents all the current values of the input's
415-- consitutent 'Dynamic's.
416collectDynPure :: ( RebuildSortedHList (HListElems b)
417 , IsHList a, IsHList b
418 , AllAreFunctors (Dynamic t) (HListElems b)
419 , Reflex t
420 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
421 ) => a -> Dynamic t b
422collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
424-- | Poor man's 'Generic's for product types only.
425class IsHList a where
426 type HListElems a :: [*]
427 toHList :: a -> HList (HListElems a)
428 fromHList :: HList (HListElems a) -> a
430instance IsHList (a, b) where
431 type HListElems (a, b) = [a, b]
432 toHList (a, b) = hBuild a b
433 fromHList l = case l of
434 a `HCons` b `HCons` HNil -> (a, b)
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) where
440 type HListElems (a, b, c, d) = [a, b, c, d]
441 toHList (a, b, c, d) = hBuild a b c d
442 fromHList l = case l of
443 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
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
448instance IsHList (a, b, c, d, e, f) where
449 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
450 toHList (a, b, c, d, e, f) = hBuild a b c d e f
451 fromHList l = case l of
452 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
453#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
454 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
455#endif
457--------------------------------------------------------------------------------
458-- Deprecated functions
459--------------------------------------------------------------------------------
461-- | Map a function over a 'Dynamic'.
462{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
463mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
464mapDyn f = return . fmap f
466-- | 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.
467{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
468mapDynM :: 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)
469mapDynM f d = do
470 let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
471 eb' = fmap constant e'
472 v0 = pull $ f =<< sample (current d)
473 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
474 let b' = pull $ sample =<< sample bb'
475 return $ unsafeDynamic b' e'
477-- | Flipped version of 'mapDyn'.
478{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
479forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
480forDyn a = return . ffor a
482-- | Flipped version of 'mapDynM'
483{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
484forDynM :: 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)
485forDynM d f = mapDynM f d
487-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
488-- of the tuple.
489{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
490splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
491splitDyn = return . splitDynPure
493-- | Merge the 'Dynamic' values using their 'Monoid' instance.
494{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
495mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
496mconcatDyn = return . mconcat
498-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
499{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
500distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
501distributeDMapOverDyn = return . distributeDMapOverDynPure
503-- | Merge two 'Dynamic's into a new one using the provided function. The new
504-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
505-- its value.
506{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
507combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
508combineDyn f a b = return $ zipDynWith f a b
510-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
511--
512-- > do
513-- > person <- Person `mapDyn` dynFirstName
514-- > `apDyn` dynListName
515-- > `apDyn` dynAge
516-- > `apDyn` dynAddress
517{-# 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" #-}
518{-# ANN apDyn "HLint: ignore Use fmap" #-}
519apDyn :: forall t m a b. (Reflex t, Monad m)
520 => m (Dynamic t (a -> b))
521 -> Dynamic t a
522 -> m (Dynamic t b)
523apDyn m a = liftM (<*> a) m
525--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
526-- | This function no longer needs to be monadic, so it has been replaced by
527-- 'demuxed', which is pure.
528{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
529getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
530getDemuxed d = return . demuxed d
532-- | This function no longer needs to be monadic, so it has been replaced by
533-- 'distributeFHListOverDynPure', which is pure.
534{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
535distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
536distributeFHListOverDyn = return . distributeFHListOverDynPure
538-- | This function no longer needs to be monadic, so it has been replaced by
539-- 'collectDynPure', which is pure.
540{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
541collectDyn :: ( RebuildSortedHList (HListElems b)
542 , IsHList a, IsHList b
543 , AllAreFunctors (Dynamic t) (HListElems b)
544 , Reflex t, Monad m
545 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
546 ) => a -> m (Dynamic t b)
547collectDyn = return . collectDynPure
549-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
550-- semantics.
551{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
552tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
553tagDyn = tagPromptlyDyn
555-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
556-- semantics.
557{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
558attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
559attachDyn = attachPromptlyDyn
561-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
562-- semantics.
563{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
564attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
565attachDynWith = attachPromptlyDynWith
567-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
568-- its semantics.
569{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
570attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
571attachDynWithMaybe = attachPromptlyDynWithMaybe
573-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
574-- current value will always be equal to the current value's current value, and
575-- will change whenever either the inner or the outer (or both) values change.
576{-# DEPRECATED joinDyn "Use 'join' instead" #-}
577joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
578joinDyn = join
580-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
581-- been renamed to 'uniqDyn'
582{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
583nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
584nubDyn = uniqDyn