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