on ae0af76 help sign in
patch/src/Reflex/Dynamic.hs
588 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' when it is first read and on each
189-- subsequent change that is observed (as traceEvent), prefixed with the
190-- provided string. This should /only/ be used for debugging.
191--
192-- Note: Just like Debug.Trace.trace, the value will only be shown if something
193-- else in the system is depending on it.
194traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
195traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
197-- | Print the result of applying the provided function to the value
198-- of the 'Dynamic' when it is first read and on each subsequent change
199-- that is observed (as traceEvent). This should /only/ be used for
200-- debugging.
201--
202-- Note: Just like Debug.Trace.trace, the value will only be shown if something
203-- else in the system is depending on it.
204traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
205traceDynWith f d =
206 let e' = traceEventWith f $ updated d
207 getV0 = do
208 x <- sample $ current d
209 trace (f x) $ return x
210 in unsafeBuildDynamic getV0 e'
212-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
213-- each time the 'Event' occurs.
214--
215-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
216-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
217-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
218-- will be used, since the 'Behavior' won't be updated until the end of the frame.
219-- Additionally, this means that the output 'Event' may not be used to directly change
220-- the input 'Dynamic', because that would mean its value depends on itself. When creating
221-- cyclic data flows, generally `tag (current d) e` is preferred.
222tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
223tagPromptlyDyn = attachPromptlyDynWith const
225-- | Attach the current value of the 'Dynamic' to the value of the
226-- 'Event' each time it occurs.
227--
228-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
229attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
230attachPromptlyDyn = attachPromptlyDynWith (,)
232-- | Combine the current value of the 'Dynamic' with the value of the
233-- 'Event' each time it occurs.
234--
235-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
236attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
237attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
239-- | Create a new 'Event' by combining the value at each occurence with the
240-- current value of the 'Dynamic' value and possibly filtering if the combining
241-- function returns 'Nothing'.
242--
243-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
244-- (current d)`. See 'tagPromptlyDyn' for details.
245attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
246attachPromptlyDynWithMaybe f d e =
247 let e' = attach (current d) e
248 in fforMaybe (align e' $ updated d) $ \case
249 This (a, b) -> f a b -- Only the tagging event is firing, so use that
250 These (_, b) a -> f a b -- Both events are firing, so use the newer value
251 That _ -> Nothing -- The tagging event isn't firing, so don't fire
253--------------------------------------------------------------------------------
254-- Demux
255--------------------------------------------------------------------------------
257-- | Represents a time changing value together with an 'EventSelector' that can
258-- efficiently detect when the underlying 'Dynamic' has a particular value.
259-- This is useful for representing data like the current selection of a long
260-- list.
261--
262-- Semantically,
263--
264-- > demuxed (demux d) k === fmap (== k) d
265--
266-- However, the when getDemuxed is used multiple times, the complexity is only
267-- /O(log(n))/, rather than /O(n)/ for fmap.
268data Demux t k = Demux { demuxValue :: Behavior t k
269 , demuxSelector :: EventSelector t (Const2 k Bool)
270 }
272-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
273-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
274demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
275demux k = Demux (current k)
276 (fan $ attachWith (\k0 k1 -> if k0 == k1
277 then DMap.empty
278 else DMap.fromList [Const2 k0 :=> Identity False,
279 Const2 k1 :=> Identity True])
280 (current k) (updated k))
282-- | Select a particular output of the 'Demux'; this is equivalent to (but much
283-- faster than) mapping over the original 'Dynamic' and checking whether it is
284-- equal to the given key.
285demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
286demuxed d k =
287 let e = select (demuxSelector d) (Const2 k)
288 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
290--------------------------------------------------------------------------------
291-- collectDyn
292--------------------------------------------------------------------------------
294--TODO: This whole section is badly in need of cleanup
296-- | A heterogeneous list whose type and length are fixed statically. This is
297-- reproduced from the 'HList' package due to integration issues, and because
298-- very little other functionality from that library is needed.
299data HList (l::[*]) where
300 HNil :: HList '[]
301 HCons :: e -> HList l -> HList (e ': l)
303infixr 2 `HCons`
305type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
306type instance HRevApp '[] l = l
307type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
309hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
310hRevApp HNil l = l
311hRevApp (HCons x l) l' = hRevApp l (HCons x l')
313hReverse :: HList l -> HList (HRevApp l '[])
314hReverse l = hRevApp l HNil
316hBuild :: (HBuild' '[] r) => r
317hBuild = hBuild' HNil
319class HBuild' l r where
320 hBuild' :: HList l -> r
322instance (l' ~ HRevApp l '[])
323 => HBuild' l (HList l') where
324 hBuild' = hReverse
326instance HBuild' (a ': l) r
327 => HBuild' l (a->r) where
328 hBuild' l x = hBuild' (HCons x l)
330-- | Like 'HList', but with a functor wrapping each element.
331data FHList f l where
332 FHNil :: FHList f '[]
333 FHCons :: f e -> FHList f l -> FHList f (e ': l)
335instance GEq (HListPtr l) where
336 HHeadPtr `geq` HHeadPtr = Just Refl
337 HHeadPtr `geq` HTailPtr _ = Nothing
338 HTailPtr _ `geq` HHeadPtr = Nothing
339 HTailPtr a `geq` HTailPtr b = a `geq` b
341instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
342 HHeadPtr `gcompare` HHeadPtr = GEQ
343 HHeadPtr `gcompare` HTailPtr _ = GLT
344 HTailPtr _ `gcompare` HHeadPtr = GGT
345 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
347-- | A typed index into a typed heterogeneous list.
348data HListPtr l a where
349 HHeadPtr :: HListPtr (h ': t) h
350 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
352fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
353fhlistToDMap = DMap.fromList . go
354 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
355 go = \case
356 FHNil -> []
357 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
359-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
360-- they must be contiguous and sorted.
361class RebuildSortedHList l where
362 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
363 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
365instance RebuildSortedHList '[] where
366 rebuildSortedFHList l = case l of
367 [] -> FHNil
368 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
369 rebuildSortedHList l = case l of
370 [] -> HNil
371 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
373instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
374 rebuildSortedFHList l = case l of
375 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
376 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
377 rebuildSortedHList l = case l of
378 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
379 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
381dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
382dmapToHList = rebuildSortedHList . DMap.toList
384-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
385-- single 'Dynamic' whose value represents the current values of all of the
386-- input 'Dynamic's.
387distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
388distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
390-- | Indicates that all elements in a type-level list are applications of the
391-- same functor.
392class AllAreFunctors (f :: a -> *) (l :: [a]) where
393 type FunctorList f l :: [*]
394 toFHList :: HList (FunctorList f l) -> FHList f l
395 fromFHList :: FHList f l -> HList (FunctorList f l)
397instance AllAreFunctors f '[] where
398 type FunctorList f '[] = '[]
399 toFHList l = case l of
400 HNil -> FHNil
401#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
402 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
403#endif
404 fromFHList FHNil = HNil
406instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
407 type FunctorList f (h ': t) = f h ': FunctorList f t
408 toFHList l = case l of
409 a `HCons` b -> a `FHCons` toFHList b
410#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
411 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
412#endif
413 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
415-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
416-- single 'Dynamic' whose value represents all the current values of the input's
417-- consitutent 'Dynamic's.
418collectDynPure :: ( RebuildSortedHList (HListElems b)
419 , IsHList a, IsHList b
420 , AllAreFunctors (Dynamic t) (HListElems b)
421 , Reflex t
422 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
423 ) => a -> Dynamic t b
424collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
426-- | Poor man's 'Generic's for product types only.
427class IsHList a where
428 type HListElems a :: [*]
429 toHList :: a -> HList (HListElems a)
430 fromHList :: HList (HListElems a) -> a
432instance IsHList (a, b) where
433 type HListElems (a, b) = [a, b]
434 toHList (a, b) = hBuild a b
435 fromHList l = case l of
436 a `HCons` b `HCons` HNil -> (a, b)
437#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
438 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
439#endif
441instance IsHList (a, b, c, d) where
442 type HListElems (a, b, c, d) = [a, b, c, d]
443 toHList (a, b, c, d) = hBuild a b c d
444 fromHList l = case l of
445 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
446#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
447 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
448#endif
450instance IsHList (a, b, c, d, e, f) where
451 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
452 toHList (a, b, c, d, e, f) = hBuild a b c d e f
453 fromHList l = case l of
454 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
455#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
456 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
457#endif
459--------------------------------------------------------------------------------
460-- Deprecated functions
461--------------------------------------------------------------------------------
463-- | Map a function over a 'Dynamic'.
464{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
465mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
466mapDyn f = return . fmap f
468-- | 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.
469{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
470mapDynM :: 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)
471mapDynM f d = do
472 let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
473 eb' = fmap constant e'
474 v0 = pull $ f =<< sample (current d)
475 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
476 let b' = pull $ sample =<< sample bb'
477 return $ unsafeDynamic b' e'
479-- | Flipped version of 'mapDyn'.
480{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
481forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
482forDyn a = return . ffor a
484-- | Flipped version of 'mapDynM'
485{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
486forDynM :: 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)
487forDynM d f = mapDynM f d
489-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
490-- of the tuple.
491{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
492splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
493splitDyn = return . splitDynPure
495-- | Merge the 'Dynamic' values using their 'Monoid' instance.
496{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
497mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
498mconcatDyn = return . mconcat
500-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
501{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
502distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
503distributeDMapOverDyn = return . distributeDMapOverDynPure
505-- | Merge two 'Dynamic's into a new one using the provided function. The new
506-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
507-- its value.
508{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
509combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
510combineDyn f a b = return $ zipDynWith f a b
512-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
513--
514-- > do
515-- > person <- Person `mapDyn` dynFirstName
516-- > `apDyn` dynListName
517-- > `apDyn` dynAge
518-- > `apDyn` dynAddress
519{-# 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" #-}
520#ifdef USE_TEMPLATE_HASKELL
521{-# ANN apDyn "HLint: ignore Use fmap" #-}
522#endif
523apDyn :: forall t m a b. (Reflex t, Monad m)
524 => m (Dynamic t (a -> b))
525 -> Dynamic t a
526 -> m (Dynamic t b)
527apDyn m a = fmap (<*> a) m
529--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
530-- | This function no longer needs to be monadic, so it has been replaced by
531-- 'demuxed', which is pure.
532{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
533getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
534getDemuxed d = return . demuxed d
536-- | This function no longer needs to be monadic, so it has been replaced by
537-- 'distributeFHListOverDynPure', which is pure.
538{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
539distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
540distributeFHListOverDyn = return . distributeFHListOverDynPure
542-- | This function no longer needs to be monadic, so it has been replaced by
543-- 'collectDynPure', which is pure.
544{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
545collectDyn :: ( RebuildSortedHList (HListElems b)
546 , IsHList a, IsHList b
547 , AllAreFunctors (Dynamic t) (HListElems b)
548 , Reflex t, Monad m
549 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
550 ) => a -> m (Dynamic t b)
551collectDyn = return . collectDynPure
553-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
554-- semantics.
555{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
556tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
557tagDyn = tagPromptlyDyn
559-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
560-- semantics.
561{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
562attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
563attachDyn = attachPromptlyDyn
565-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
566-- semantics.
567{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
568attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
569attachDynWith = attachPromptlyDynWith
571-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
572-- its semantics.
573{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
574attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
575attachDynWithMaybe = attachPromptlyDynWithMaybe
577-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
578-- current value will always be equal to the current value's current value, and
579-- will change whenever either the inner or the outer (or both) values change.
580{-# DEPRECATED joinDyn "Use 'join' instead" #-}
581joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
582joinDyn = join
584-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
585-- been renamed to 'uniqDyn'
586{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
587nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
588nubDyn = uniqDyn