on 2ab7121 help sign in
patch/src/Reflex/Dynamic.hs
687 lines 31 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 RecursiveDo #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE StandaloneDeriving #-}
14{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeOperators #-}
16{-# LANGUAGE UndecidableInstances #-}
17#ifdef USE_REFLEX_OPTIMIZER
18{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
19#endif
20-- | This module contains various functions for working with 'Dynamic' values.
21-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
22module Reflex.Dynamic
23 ( -- * Basics
24 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
25 , current
26 , updated
27 , holdDyn
28 , constDyn
29 , count
30 , toggle
31 , switchPromptlyDyn
32 , tagPromptlyDyn
33 , attachPromptlyDyn
34 , attachPromptlyDynWith
35 , attachPromptlyDynWithMaybe
36 , maybeDyn
37 , eitherDyn
38 , factorDyn
39 , scanDyn
40 , scanDynMaybe
41 , holdUniqDyn
42 , holdUniqDynBy
43 , improvingMaybe
44 , foldDyn
45 , foldDynM
46 , foldDynMaybe
47 , foldDynMaybeM
48 , joinDynThroughMap
49 , traceDyn
50 , traceDynWith
51 , splitDynPure
52 , distributeMapOverDynPure
53 , distributeDMapOverDynPure
54 , distributeListOverDynPure
55 , Demux
56 , demux
57 , demuxed
58 -- Things that probably aren't very useful:
59 , HList (..)
60 , FHList (..)
61 , collectDynPure
62 , RebuildSortedHList (..)
63 , IsHList (..)
64 , AllAreFunctors (..)
65 , HListPtr (..)
66 , distributeFHListOverDynPure
67 -- Unsafe
68 , unsafeDynamic
69 -- * Deprecated functions
70 , apDyn
71 , attachDyn
72 , attachDynWith
73 , attachDynWithMaybe
74 , collectDyn
75 , combineDyn
76 , distributeDMapOverDyn
77 , distributeFHListOverDyn
78 , forDyn
79 , forDynM
80 , getDemuxed
81 , joinDyn
82 , mapDyn
83 , mapDynM
84 , mconcatDyn
85 , nubDyn
86 , splitDyn
87 , tagDyn
88 , uniqDyn
89 , uniqDynBy
90 ) where
92import Data.Functor.Compose
93import Data.Functor.Misc
94import Reflex.Class
96import Control.Applicative ((<*>))
97import Control.Monad
98import Control.Monad.Fix
99import Control.Monad.Identity
100import Data.Align
101import Data.Dependent.Map (DMap)
102import qualified Data.Dependent.Map as DMap
103import Data.Dependent.Sum (DSum (..))
104import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
105import Data.Map (Map)
106import Data.Maybe
107import Data.Monoid
108import Data.These
110import Debug.Trace
112-- | Create a new 'Dynamic' that only signals changes if the values actually
113-- changed.
114holdUniqDyn :: (Reflex t, MonadHold t m, MonadFix m, Eq a) => Dynamic t a -> m (Dynamic t a)
115holdUniqDyn = holdUniqDynBy (==)
117-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
118-- changes and the given function returns 'False' when given both the old and
119-- the new values.
120holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
121holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
123-- | Dynamic Maybe that can only update from Nothing to Just or Just to Just (i.e., cannot revert to Nothing)
124improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
125improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
127-- | Create a 'Dynamic' that accumulates values from another 'Dynamic'. This
128-- function does not force its input 'Dynamic' until the output 'Dynamic' is
129-- forced.
130scanDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> b) -> Dynamic t a -> m (Dynamic t b)
131scanDyn z f = scanDynMaybe z (\a b -> Just $ f a b)
133-- | Like 'scanDyn', but the the accumulator function may decline to update the
134-- result 'Dynamic''s value.
135scanDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> Maybe b) -> Dynamic t a -> m (Dynamic t b)
136scanDynMaybe z f d = do
137 rec d' <- buildDynamic (z <$> sample (current d)) $ flip push (updated d) $ \a -> do
138 b <- sample $ current d'
139 return $ f a b
140 return d'
142-- | Create a 'Dynamic' using the initial value and change it each time the
143-- 'Event' occurs using a folding function on the previous value and the value
144-- of the 'Event'.
145foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
146foldDyn = accum . flip
148-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
149-- can 'sample' existing 'Behaviors' and 'hold' new ones.
150foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
151foldDynM = accumM . flip
153-- | Create a 'Dynamic' using the provided initial value and change it each time
154-- the provided 'Event' occurs, using a function to combine the old value with
155-- the 'Event''s value. If the function returns 'Nothing', the value is not
156-- changed; this is distinct from returning 'Just' the old value, since the
157-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
158-- in the 'Nothing' case.
159foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
160foldDynMaybe = accumMaybe . flip
162-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
163-- can 'sample' existing 'Behaviors' and 'hold' new ones.
164foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
165foldDynMaybeM = accumMaybeM . flip
167-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
168count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
169count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
171-- | Create a new 'Dynamic' using the initial value that flips its
172-- value every time the 'Event' occurs.
173toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
174toggle = foldDyn (const not)
176-- | Switches to the new 'Event' whenever it receives one. Switching occurs
177-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
178-- old and new inner Events fire simultaneously, the output will fire with the
179-- value of the *new* 'Event'.
180switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
181switchPromptlyDyn de =
182 let eLag = switch $ current de
183 eCoincidences = coincidence $ updated de
184 in leftmost [eCoincidences, eLag]
186-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
187splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
188splitDynPure d = (fmap fst d, fmap snd d)
190-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
191-- non-'Dynamic' elements.
192distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
193distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
195-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
196-- non-'Dynamic' elements, preserving the order of the elements.
197distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
198distributeListOverDynPure =
199 fmap (map fromDSum . DMap.toAscList) .
200 distributeDMapOverDynPure .
201 DMap.fromDistinctAscList .
202 zipWith toDSum [0..]
203 where
204 toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
205 toDSum k v = Const2 k :=> v
206 fromDSum :: DSum (Const2 Int a) Identity -> a
207 fromDSum (Const2 _ :=> Identity v) = v
209--TODO: Generalize this to functors other than Maps
210-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
211-- with the current values of the 'Dynamic's in a map.
212joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
213joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
215-- | Print the value of the 'Dynamic' when it is first read and on each
216-- subsequent change that is observed (as traceEvent), prefixed with the
217-- provided string. This should /only/ be used for debugging.
218--
219-- Note: Just like Debug.Trace.trace, the value will only be shown if something
220-- else in the system is depending on it.
221traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
222traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
224-- | Print the result of applying the provided function to the value
225-- of the 'Dynamic' when it is first read and on each subsequent change
226-- that is observed (as traceEvent). This should /only/ be used for
227-- debugging.
228--
229-- Note: Just like Debug.Trace.trace, the value will only be shown if something
230-- else in the system is depending on it.
231traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
232traceDynWith f d =
233 let e' = traceEventWith f $ updated d
234 getV0 = do
235 x <- sample $ current d
236 trace (f x) $ return x
237 in unsafeBuildDynamic getV0 e'
239-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
240-- each time the 'Event' occurs.
241--
242-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
243-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
244-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
245-- will be used, since the 'Behavior' won't be updated until the end of the frame.
246-- Additionally, this means that the output 'Event' may not be used to directly change
247-- the input 'Dynamic', because that would mean its value depends on itself. When creating
248-- cyclic data flows, generally `tag (current d) e` is preferred.
249tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
250tagPromptlyDyn = attachPromptlyDynWith const
252-- | Attach the current value of the 'Dynamic' to the value of the
253-- 'Event' each time it occurs.
254--
255-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
256attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
257attachPromptlyDyn = attachPromptlyDynWith (,)
259-- | Combine the current value of the 'Dynamic' with the value of the
260-- 'Event' each time it occurs.
261--
262-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
263attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
264attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
266-- | Create a new 'Event' by combining the value at each occurence with the
267-- current value of the 'Dynamic' value and possibly filtering if the combining
268-- function returns 'Nothing'.
269--
270-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
271-- (current d)`. See 'tagPromptlyDyn' for details.
272attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
273attachPromptlyDynWithMaybe f d e =
274 let e' = attach (current d) e
275 in fforMaybe (align e' $ updated d) $ \case
276 This (a, b) -> f a b -- Only the tagging event is firing, so use that
277 These (_, b) a -> f a b -- Both events are firing, so use the newer value
278 That _ -> Nothing -- The tagging event isn't firing, so don't fire
280-- | Factor a @Dynamic t (Maybe a)@ into a @Dynamic t (Maybe (Dynamic t a))@,
281-- such that the outer 'Dynamic' is updated only when the 'Maybe''s constructor
282-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
283-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
284-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
285-- existing inner 'Dynamic' will become constant, and will not change when the
286-- outer constructor changes back to 'Nothing'.
287maybeDyn :: forall t a m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe (Dynamic t a)))
288maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
289 where pack = \case
290 Nothing -> Left ()
291 Just a -> Right a
292 unpack = \case
293 Left _ -> Nothing
294 Right a -> Just a
296eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
297eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
298 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
299 unpack = \case
300 LeftTag :=> Compose a -> Left $ coerceDynamic a
301 RightTag :=> Compose b -> Right $ coerceDynamic b
303factorDyn :: forall t m k v. (Reflex t, MonadFix m, MonadHold t m, GEq k) => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
304factorDyn d = do
305 let inner :: forall m' a. (MonadFix m', MonadHold t m') => k a -> v a -> m' (Dynamic t (v a))
306 inner k v0 = holdDyn v0 . fmapMaybe id =<< takeWhileE isJust newVal
307 where newVal = ffor (updated d) $ \(newK :=> newV) -> case newK `geq` k of
308 Just Refl -> Just newV
309 Nothing -> Nothing
310 getInitial = do
311 k0 :=> (v0 :: v a) <- sample $ current d
312 i0 <- inner k0 v0
313 return $ k0 :=> Compose i0
314 update = flip push (updated d) $ \(newKey :=> newVal) -> do
315 (oldKey :=> _) <- sample $ current d
316 case newKey `geq` oldKey of
317 Just Refl -> return Nothing
318 Nothing -> do
319 newInner <- inner newKey newVal
320 return $ Just $ newKey :=> Compose newInner
321 o0 <- getInitial --TODO: Figure out how to get this to run inside something like the first argument to buildDynamic
322 holdDyn o0 update
324--------------------------------------------------------------------------------
325-- Demux
326--------------------------------------------------------------------------------
328-- | Represents a time changing value together with an 'EventSelector' that can
329-- efficiently detect when the underlying 'Dynamic' has a particular value.
330-- This is useful for representing data like the current selection of a long
331-- list.
332--
333-- Semantically,
334--
335-- > demuxed (demux d) k === fmap (== k) d
336--
337-- However, the when getDemuxed is used multiple times, the complexity is only
338-- /O(log(n))/, rather than /O(n)/ for fmap.
339data Demux t k = Demux { demuxValue :: Behavior t k
340 , demuxSelector :: EventSelector t (Const2 k Bool)
341 }
343-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
344-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
345demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
346demux k = Demux (current k)
347 (fan $ attachWith (\k0 k1 -> if k0 == k1
348 then DMap.empty
349 else DMap.fromList [Const2 k0 :=> Identity False,
350 Const2 k1 :=> Identity True])
351 (current k) (updated k))
353-- | Select a particular output of the 'Demux'; this is equivalent to (but much
354-- faster than) mapping over the original 'Dynamic' and checking whether it is
355-- equal to the given key.
356demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
357demuxed d k =
358 let e = select (demuxSelector d) (Const2 k)
359 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
361--------------------------------------------------------------------------------
362-- collectDyn
363--------------------------------------------------------------------------------
365--TODO: This whole section is badly in need of cleanup
367-- | A heterogeneous list whose type and length are fixed statically. This is
368-- reproduced from the 'HList' package due to integration issues, and because
369-- very little other functionality from that library is needed.
370data HList (l::[*]) where
371 HNil :: HList '[]
372 HCons :: e -> HList l -> HList (e ': l)
374infixr 2 `HCons`
376type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
377type instance HRevApp '[] l = l
378type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
380hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
381hRevApp HNil l = l
382hRevApp (HCons x l) l' = hRevApp l (HCons x l')
384hReverse :: HList l -> HList (HRevApp l '[])
385hReverse l = hRevApp l HNil
387hBuild :: (HBuild' '[] r) => r
388hBuild = hBuild' HNil
390class HBuild' l r where
391 hBuild' :: HList l -> r
393instance (l' ~ HRevApp l '[])
394 => HBuild' l (HList l') where
395 hBuild' = hReverse
397instance HBuild' (a ': l) r
398 => HBuild' l (a->r) where
399 hBuild' l x = hBuild' (HCons x l)
401-- | Like 'HList', but with a functor wrapping each element.
402data FHList f l where
403 FHNil :: FHList f '[]
404 FHCons :: f e -> FHList f l -> FHList f (e ': l)
406instance GEq (HListPtr l) where
407#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
408 geqToEq _ = id
409 geqUnify HHeadPtr HHeadPtr _ same = same
410 geqUnify (HTailPtr a) (HTailPtr b) different same = geqUnify a b different same
411 geqUnify _ _ different _ = different
412#endif
413 HHeadPtr `geq` HHeadPtr = Just Refl
414 HHeadPtr `geq` HTailPtr _ = Nothing
415 HTailPtr _ `geq` HHeadPtr = Nothing
416 HTailPtr a `geq` HTailPtr b = a `geq` b
418instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
419#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
420 gcompareToOrd _ = id
421#endif
422 HHeadPtr `gcompare` HHeadPtr = GEQ
423 HHeadPtr `gcompare` HTailPtr _ = GLT
424 HTailPtr _ `gcompare` HHeadPtr = GGT
425 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
427-- | A typed index into a typed heterogeneous list.
428data HListPtr l a where
429 HHeadPtr :: HListPtr (h ': t) h
430 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
432deriving instance Eq (HListPtr l a)
433deriving instance Ord (HListPtr l a)
435fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
436fhlistToDMap = DMap.fromList . go
437 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
438 go = \case
439 FHNil -> []
440 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
442-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
443-- they must be contiguous and sorted.
444class RebuildSortedHList l where
445 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
446 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
448instance RebuildSortedHList '[] where
449 rebuildSortedFHList l = case l of
450 [] -> FHNil
451 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
452 rebuildSortedHList l = case l of
453 [] -> HNil
454 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
456instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
457 rebuildSortedFHList l = case l of
458 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
459 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
460 rebuildSortedHList l = case l of
461 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
462 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
464dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
465dmapToHList = rebuildSortedHList . DMap.toList
467-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
468-- single 'Dynamic' whose value represents the current values of all of the
469-- input 'Dynamic's.
470distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
471distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
473-- | Indicates that all elements in a type-level list are applications of the
474-- same functor.
475class AllAreFunctors (f :: a -> *) (l :: [a]) where
476 type FunctorList f l :: [*]
477 toFHList :: HList (FunctorList f l) -> FHList f l
478 fromFHList :: FHList f l -> HList (FunctorList f l)
480instance AllAreFunctors f '[] where
481 type FunctorList f '[] = '[]
482 toFHList l = case l of
483 HNil -> FHNil
484#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
485 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
486#endif
487 fromFHList FHNil = HNil
489instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
490 type FunctorList f (h ': t) = f h ': FunctorList f t
491 toFHList l = case l of
492 a `HCons` b -> a `FHCons` toFHList b
493#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
494 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
495#endif
496 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
498-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
499-- single 'Dynamic' whose value represents all the current values of the input's
500-- consitutent 'Dynamic's.
501collectDynPure :: ( RebuildSortedHList (HListElems b)
502 , IsHList a, IsHList b
503 , AllAreFunctors (Dynamic t) (HListElems b)
504 , Reflex t
505 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
506 ) => a -> Dynamic t b
507collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
509-- | Poor man's 'Generic's for product types only.
510class IsHList a where
511 type HListElems a :: [*]
512 toHList :: a -> HList (HListElems a)
513 fromHList :: HList (HListElems a) -> a
515instance IsHList (a, b) where
516 type HListElems (a, b) = [a, b]
517 toHList (a, b) = hBuild a b
518 fromHList l = case l of
519 a `HCons` b `HCons` HNil -> (a, b)
520#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
521 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
522#endif
524instance IsHList (a, b, c, d) where
525 type HListElems (a, b, c, d) = [a, b, c, d]
526 toHList (a, b, c, d) = hBuild a b c d
527 fromHList l = case l of
528 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
529#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
530 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
531#endif
533instance IsHList (a, b, c, d, e, f) where
534 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
535 toHList (a, b, c, d, e, f) = hBuild a b c d e f
536 fromHList l = case l of
537 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
538#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
539 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
540#endif
542--------------------------------------------------------------------------------
543-- Deprecated functions
544--------------------------------------------------------------------------------
546-- | Map a function over a 'Dynamic'.
547{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
548mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
549mapDyn f = return . fmap f
551-- | 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.
552{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
553mapDynM :: 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)
554mapDynM f d = do
555 let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
556 eb' = fmap constant e'
557 v0 = pull $ f =<< sample (current d)
558 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
559 let b' = pull $ sample =<< sample bb'
560 return $ unsafeDynamic b' e'
562-- | Flipped version of 'mapDyn'.
563{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
564forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
565forDyn a = return . ffor a
567-- | Flipped version of 'mapDynM'
568{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
569forDynM :: 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)
570forDynM d f = mapDynM f d
572-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
573-- of the tuple.
574{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
575splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
576splitDyn = return . splitDynPure
578-- | Merge the 'Dynamic' values using their 'Monoid' instance.
579{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
580mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
581mconcatDyn = return . mconcat
583-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
584{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
585distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
586distributeDMapOverDyn = return . distributeDMapOverDynPure
588-- | Merge two 'Dynamic's into a new one using the provided function. The new
589-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
590-- its value.
591{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
592combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
593combineDyn f a b = return $ zipDynWith f a b
595-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
596--
597-- > do
598-- > person <- Person `mapDyn` dynFirstName
599-- > `apDyn` dynListName
600-- > `apDyn` dynAge
601-- > `apDyn` dynAddress
602{-# 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" #-}
603#ifdef USE_TEMPLATE_HASKELL
604{-# ANN apDyn "HLint: ignore Use fmap" #-}
605#endif
606apDyn :: forall t m a b. (Reflex t, Monad m)
607 => m (Dynamic t (a -> b))
608 -> Dynamic t a
609 -> m (Dynamic t b)
610apDyn m a = fmap (<*> a) m
612--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
613-- | This function no longer needs to be monadic, so it has been replaced by
614-- 'demuxed', which is pure.
615{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
616getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
617getDemuxed d = return . demuxed d
619-- | This function no longer needs to be monadic, so it has been replaced by
620-- 'distributeFHListOverDynPure', which is pure.
621{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
622distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
623distributeFHListOverDyn = return . distributeFHListOverDynPure
625-- | This function no longer needs to be monadic, so it has been replaced by
626-- 'collectDynPure', which is pure.
627{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
628collectDyn :: ( RebuildSortedHList (HListElems b)
629 , IsHList a, IsHList b
630 , AllAreFunctors (Dynamic t) (HListElems b)
631 , Reflex t, Monad m
632 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
633 ) => a -> m (Dynamic t b)
634collectDyn = return . collectDynPure
636-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
637-- semantics.
638{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
639tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
640tagDyn = tagPromptlyDyn
642-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
643-- semantics.
644{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
645attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
646attachDyn = attachPromptlyDyn
648-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
649-- semantics.
650{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
651attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
652attachDynWith = attachPromptlyDynWith
654-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
655-- its semantics.
656{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
657attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
658attachDynWithMaybe = attachPromptlyDynWithMaybe
660-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
661-- current value will always be equal to the current value's current value, and
662-- will change whenever either the inner or the outer (or both) values change.
663{-# DEPRECATED joinDyn "Use 'join' instead" #-}
664joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
665joinDyn = join
667-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
668-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
669-- circumstances. Also, note that, unlike 'nub', this function does not prevent
670-- all recurrences of a value, only consecutive recurrences.
671{-# DEPRECATED nubDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
672nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
673nubDyn = uniqDyn
675-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
676-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
677-- circumstances.
678{-# DEPRECATED uniqDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
679uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
680uniqDyn = uniqDynBy (==)
682-- | WARNING: This function is impure. Use 'holdUniqDynBy' instead.
683{-# DEPRECATED uniqDynBy "Use 'holdUniqDynBy' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
684uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
685uniqDynBy eq d =
686 let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
687 in unsafeDynamic (current d) e'