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