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