1{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, DataKinds, PolyKinds #-}
2module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
23 , distributeDMapOverDyn
32 -- Things that probably aren't very useful:
35 , distributeFHListOverDyn
40import Prelude hiding (mapM, mapM_)
43import Data.Functor.Misc
45import Control.Monad hiding (mapM, mapM_, forM, forM_)
46import Control.Monad.Fix
47import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_)
49import Data.Traversable (mapM, forM)
52import qualified Data.Map as Map
53import Data.Dependent.Map (DMap)
54import qualified Data.Dependent.Map as DMap
55import Data.Dependent.Sum (DSum (..))
56import Data.GADT.Compare (GCompare (..), GEq (..), (:~:) (..), GOrdering (..))
58--import Data.HList (HList (..), hBuild)
60data HList (l::[*]) where
62 HCons :: e -> HList l -> HList (e ': l)
66type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
67type instance HRevApp '[] l = l
68type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
70hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
72hRevApp (HCons x l) l' = hRevApp l (HCons x l')
74hReverse :: HList l -> HList (HRevApp l '[])
75hReverse l = hRevApp l HNil
77hBuild :: (HBuild' '[] r) => r
80class HBuild' l r where
81 hBuild' :: HList l -> r
83instance (l' ~ HRevApp l '[])
84 => HBuild' l (HList l') where
85 hBuild' l = hReverse l
87instance HBuild' (a ': l) r
88 => HBuild' l (a->r) where
89 hBuild' l x = hBuild' (HCons x l)
91-- | A container for a value that can change over time and allows notifications on changes.
92-- Basically a combination of a 'Behavior' and an 'Event'.
94 = Dynamic (Behavior t a) (Event t a)
96unsafeDynamic :: Behavior t a -> Event t a -> Dynamic t a
97unsafeDynamic = Dynamic
99-- | Extract the 'Behavior' of a 'Dynamic'.
100current :: Dynamic t a -> Behavior t a
101current (Dynamic b _) = b
103-- | Extract the 'Event' of the 'Dynamic'.
104updated :: Dynamic t a -> Event t a
105updated (Dynamic _ e) = e
107-- | 'Dynamic' with the constant supplied value.
108constDyn :: Reflex t => a -> Dynamic t a
109constDyn x = Dynamic (constant x) never
111-- | Create a 'Dynamic' using the initial value that changes every
112-- time the 'Event' occurs.
113holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
118-- | Create a new 'Dynamic' that only signals changes if the values
120nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
122 let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
123 in Dynamic (current d) e' --TODO: Avoid invalidating the outgoing Behavior
126instance Reflex t => Functor (Dynamic t) where
128 let e' = fmap f $ updated d
129 eb' = push (\b' -> liftM Just $ constant b') e'
130 b0 = fmap f $ current d
134-- | Map a function over a 'Dynamic'.
135mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
136mapDyn f = mapDynM $ return . f
138-- | Flipped version of 'forDyn'.
139forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
142-- | Map a monadic function over a 'Dynamic'.
143{-# INLINE mapDynM #-}
144mapDynM :: 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)
146 let e' = push (liftM Just . f :: a -> PushM t (Maybe b)) $ updated d
147 eb' = fmap constant e'
148 v0 = pull $ f =<< sample (current d)
149 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
150 let b' = pull $ sample =<< sample bb'
151 return $ Dynamic b' e'
153-- | Create a 'Dynamic' using the initial value and change it each
154-- time the 'Event' occurs using a folding function on the previous
155-- value and the value of the 'Event'.
156foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
157foldDyn f = foldDynM (\o v -> return $ f o v)
159-- | Create a 'Dynamic' using the initial value and change it each
160-- time the 'Event' occurs using a monadic folding function on the
161-- previous value and the value of the 'Event'.
162foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
164 rec let e' = flip push e $ \o -> do
168 return $ Dynamic b' e'
170-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
171count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
172count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
174-- | Create a new 'Dynamic' using the initial value that flips its
175-- value every time the 'Event' occurs.
176toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
177toggle = foldDyn (const not)
179-- | Switches to the new 'Event' whenever it receives one. Switching
180-- occurs *before* occurring the inner 'Event'.
181switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
182switchPromptlyDyn de =
183 let eLag = switch $ current de
184 eCoincidences = coincidence $ updated de
185 in leftmost [eCoincidences, eLag]
189mergeEventsWith :: Reflex t m => (a -> a -> a) -> Event t a -> Event t a -> m (Event t a)
190mergeEventsWith f ea eb = mapE (mergeThese f) =<< alignEvents ea eb
192firstE :: (Reflex t m) => [Event t a] -> m (Event t a)
193firstE [] = return never
194firstE (h:t) = mergeEventsLeftBiased h =<< firstE t
196concatEventsWith :: (Reflex t m) => (a -> a -> a) -> [Event t a] -> m (Event t a)
197concatEventsWith _ [] = return never
198concatEventsWith _ [e] = return e
199concatEventsWith f es = mapEM (liftM (foldl1 f . map (\(Const2 _ :=> v) -> v) . DMap.toList) . sequenceDmap) <=< mergeEventDMap $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
200--concatEventsWith f (h:t) = mergeEventsWith f h =<< concatEventsWith f t
202mconcatE :: (Reflex t m, Monoid a) => [Event t a] -> m (Event t a)
203mconcatE = concatEventsWith mappend
206-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
207-- respective value of the tuple.
208splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
209splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
211-- | Merge the 'Dynamic' values using their 'Monoid' instance.
212mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
214 ddm :: Dynamic t (DMap (Const2 Int a)) <- distributeDMapOverDyn $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
215 mapDyn (mconcat . map (\(Const2 _ :=> v) -> v) . DMap.toList) ddm
217-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of
219distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap (WrapArg (Dynamic t) k) -> m (Dynamic t (DMap k))
220distributeDMapOverDyn dm = case DMap.toList dm of
221 [] -> return $ constDyn DMap.empty
222 [WrapArg k :=> v] -> mapDyn (DMap.singleton k) v
224 let edmPre = merge $ rewrapDMap updated dm
225 edm :: Event t (DMap k) = flip push edmPre $ \o -> return . Just =<< do
227 This origDyn -> sample $ current origDyn
228 That _ -> error "distributeDMapOverDyn: should be impossible to have an event occurring that is not present in the original DMap"
229 These _ (Identity newVal) -> return newVal
230 sequenceDmap $ combineDMapsWithKey f dm (wrapDMap Identity o)
231 dm0 :: Behavior t (DMap k) = pull $ do
232 liftM DMap.fromList $ forM (DMap.toList dm) $ \(WrapArg k :=> dv) -> liftM (k :=>) $ sample $ current dv
233 bbdm :: Behavior t (Behavior t (DMap k)) <- hold dm0 $ fmap constant edm
234 let bdm = pull $ sample =<< sample bbdm
235 return $ Dynamic bdm edm
237-- | Merge two 'Dynamic's into a new one using the provided
238-- function. The new 'Dynamic' changes its value each time one of the
239-- original 'Dynamic's changes its value.
240combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
241combineDyn f da db = do
242 let eab = align (updated da) (updated db)
243 ec = flip push eab $ \o -> do
246 b <- sample $ current db
249 a <- sample $ current da
251 These a b -> return (a, b)
252 return $ Just $ f a b
253 c0 :: Behavior t c = pull $ liftM2 f (sample $ current da) (sample $ current db)
254 bbc :: Behavior t (Behavior t c) <- hold c0 $ fmap constant ec
255 let bc :: Behavior t c = pull $ sample =<< sample bbc
256 return $ Dynamic bc ec
259tagInnerDyn :: Reflex t => Event t (Dynamic t a) -> Event t a
261 let eSlow = push (liftM Just . sample . current) e
262 eFast = coincidence $ fmap updated e
263 in leftmost [eFast, eSlow]
266-- | Join a nested 'Dynamic' into a new 'Dynamic' that has the value
267-- of the inner 'Dynamic'.
268joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
270 let b' = pull $ sample . current =<< sample (current dd)
271 eOuter :: Event t a = pushAlways (sample . current) $ updated dd
272 eInner :: Event t a = switch $ fmap updated (current dd)
273 eBoth :: Event t a = coincidence $ fmap updated (updated dd)
274 e' = leftmost [eBoth, eOuter, eInner]
277--TODO: Generalize this to functors other than Maps
278-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
279-- with the current values of the 'Dynamic's in a map.
280joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
281joinDynThroughMap dd =
282 let b' = pull $ mapM (sample . current) =<< sample (current dd)
283 eOuter :: Event t (Map k a) = pushAlways (mapM (sample . current)) $ updated dd
284 eInner :: Event t (Map k a) = attachWith (flip Map.union) b' $ switch $ fmap (mergeMap . fmap updated) (current dd) --Note: the flip is important because Map.union is left-biased
285 readNonFiring :: MonadSample t m => These (Dynamic t a) a -> m a
286 readNonFiring = \case
287 This d -> sample $ current d
289 These _ a -> return a
290 eBoth :: Event t (Map k a) = coincidence $ fmap (\m -> pushAlways (mapM readNonFiring . align m) $ mergeMap $ fmap updated m) (updated dd)
291 e' = leftmost [eBoth, eOuter, eInner]
294-- | Print the value of the 'Dynamic' on each change and prefix it
295-- with the provided string. This should /only/ be used for debugging.
296traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
297traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
299-- | Print the result of applying the provided function to the value
300-- of the 'Dynamic' on each change. This should /only/ be used for
302traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
304 let e' = traceEventWith f $ updated d
305 in Dynamic (current d) e'
307-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
308-- each time the 'Event' occurs.
309tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
310tagDyn = attachDynWith const
312-- | Attach the current value of the 'Dynamic' to the value of the
313-- 'Event' each time it occurs.
314attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
315attachDyn = attachDynWith (,)
317-- | Combine the current value of the 'Dynamic' with the value of the
318-- 'Event' each time it occurs.
319attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
320attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
322-- | Create a new 'Event' by combining the value at each occurence
323-- with the current value of the 'Dynamic' value and possibly
324-- filtering if the combining function returns 'Nothing'.
325attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
326attachDynWithMaybe f d e =
327 let e' = attach (current d) e
328 in fforMaybe (align e' $ updated d) $ \case
329 This (a, b) -> f a b -- Only the tagging event is firing, so use that
330 These (_, b) a -> f a b -- Both events are firing, so use the newer value
331 That _ -> Nothing -- The tagging event isn't firing, so don't fire
333--------------------------------------------------------------------------------
335--------------------------------------------------------------------------------
337-- | Represents a time changing value together with an 'EventSelector'
338-- for changes to this value.
339data Demux t k = Demux { demuxValue :: Behavior t k
340 , demuxSelector :: EventSelector t (Const2 k Bool)
343-- | Create 'Demux' out of a 'Dynamic' that has the value of the
344-- 'Dynamic' and allows selecting between the values of the 'Event'
345-- and the 'Behavior' of the 'Dynamic' for the occurences of 'Event'
346-- where they differ. Selecting the value of the 'Behavior' will
347-- return 'False' while selecting the value of the 'Event' will return
349demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
350demux k = Demux (current k) (fan $ attachWith (\k0 k1 -> if k0 == k1 then DMap.empty else DMap.fromList [Const2 k0 :=> False, Const2 k1 :=> True]) (current k) (updated k))
352--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
353-- | Extract the 'Event' of the 'Demux' for the supplied value and
354-- create a 'Dynamic' with the initial value of the equality of the
355-- 'Behavior' and the supplied value. Each occurence of the extracted
356-- 'Event' changes the value of the 'Dynamic'.
357getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
359 let e = select (demuxSelector d) (Const2 k)
360 bb <- hold (liftM (==k) $ sample $ demuxValue d) $ fmap return e
361 let b = pull $ join $ sample bb
364--------------------------------------------------------------------------------
366--------------------------------------------------------------------------------
368--TODO: This whole section is badly in need of cleanup
371 FHNil :: FHList f '[]
372 FHCons :: f e -> FHList f l -> FHList f (e ': l)
374instance GEq (HListPtr l) where
375 HHeadPtr `geq` HHeadPtr = Just Refl
376 HHeadPtr `geq` HTailPtr _ = Nothing
377 HTailPtr _ `geq` HHeadPtr = Nothing
378 HTailPtr a `geq` HTailPtr b = a `geq` b
380instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
381 HHeadPtr `gcompare` HHeadPtr = GEQ
382 HHeadPtr `gcompare` HTailPtr _ = GLT
383 HTailPtr _ `gcompare` HHeadPtr = GGT
384 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
386data HListPtr l a where
387 HHeadPtr :: HListPtr (h ': t) h
388 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
390fhlistToDMap :: forall f l. FHList f l -> DMap (WrapArg f (HListPtr l))
391fhlistToDMap = DMap.fromList . go
392 where go :: forall l'. FHList f l' -> [DSum (WrapArg f (HListPtr l'))]
395 FHCons h t -> (WrapArg HHeadPtr :=> h) : map (\(WrapArg p :=> v) -> WrapArg (HTailPtr p) :=> v) (go t)
397class RebuildSortedHList l where
398 rebuildSortedFHList :: [DSum (WrapArg f (HListPtr l))] -> FHList f l
399 rebuildSortedHList :: [DSum (HListPtr l)] -> HList l
401instance RebuildSortedHList '[] where
402 rebuildSortedFHList [] = FHNil
403 rebuildSortedHList [] = HNil
405instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
406 rebuildSortedFHList ((WrapArg HHeadPtr :=> h) : t) = FHCons h $ rebuildSortedFHList $ map (\(WrapArg (HTailPtr p) :=> v) -> WrapArg p :=> v) t
407 rebuildSortedHList ((HHeadPtr :=> h) : t) = HCons h $ rebuildSortedHList $ map (\(HTailPtr p :=> v) -> p :=> v) t
409dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) -> HList l
410dmapToHList = rebuildSortedHList . DMap.toList
412distributeFHListOverDyn :: forall t m l. (Reflex t, MonadHold t m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
413distributeFHListOverDyn l = mapDyn dmapToHList =<< distributeDMapOverDyn (fhlistToDMap l)
415distributeFHListOverDyn l = do
417 c0 = pull $ sequenceFHList $ natMap (sample . current) l
418 bbc <- hold c0 $ fmap constant ec
419 let bc = pull $ sample =<< sample bbc
420 return $ Dynamic bc ec
423class AllAreFunctors (f :: a -> *) (l :: [a]) where
424 type FunctorList f l :: [*]
425 toFHList :: HList (FunctorList f l) -> FHList f l
426 fromFHList :: FHList f l -> HList (FunctorList f l)
428instance AllAreFunctors f '[] where
429 type FunctorList f '[] = '[]
430 toFHList HNil = FHNil
431 fromFHList FHNil = HNil
433instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
434 type FunctorList f (h ': t) = f h ': FunctorList f t
435 toFHList (a `HCons` b) = a `FHCons` toFHList b
436 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
438collectDyn :: ( RebuildSortedHList (HListElems b)
439 , IsHList a, IsHList b
440 , AllAreFunctors (Dynamic t) (HListElems b)
441 , Reflex t, MonadHold t m
442 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
443 ) => a -> m (Dynamic t b)
445 mapDyn fromHList =<< distributeFHListOverDyn (toFHList $ toHList ds)
449 type HListElems a :: [*]
450 toHList :: a -> HList (HListElems a)
451 fromHList :: HList (HListElems a) -> a
453instance IsHList (a, b) where
454 type HListElems (a, b) = [a, b]
455 toHList (a, b) = hBuild a b
456 fromHList (a `HCons` b `HCons` HNil) = (a, b)
458instance IsHList (a, b, c, d) where
459 type HListElems (a, b, c, d) = [a, b, c, d]
460 toHList (a, b, c, d) = hBuild a b c d
461 fromHList (a `HCons` b `HCons` c `HCons` d `HCons` HNil) = (a, b, c, d)
463instance IsHList (a, b, c, d, e, f) where
464 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
465 toHList (a, b, c, d, e, f) = hBuild a b c d e f
466 fromHList (a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil) = (a, b, c, d, e, f)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list