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
31 -- Things that probably aren't very useful:
33 , distributeFHListOverDyn
37import Data.Functor.Misc
40import Control.Monad.Fix
41import Control.Monad.Identity
44import Data.Dependent.Map (DMap)
45import qualified Data.Dependent.Map as DMap
46import Data.Dependent.Sum (DSum (..))
47import Data.GADT.Compare (GCompare (..), GEq (..), (:~:) (..), GOrdering (..))
49import Data.HList (HList (..), hBuild)
52 = Dynamic (Behavior t a) (Event t a)
54current :: Dynamic t a -> Behavior t a
55current (Dynamic b _) = b
57updated :: Dynamic t a -> Event t a
58updated (Dynamic _ e) = e
60constDyn :: Reflex t => a -> Dynamic t a
61constDyn x = Dynamic (constant x) never
63holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
68nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
70 let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
71 in Dynamic (current d) e' --TODO: Avoid invalidating the outgoing Behavior
74instance Reflex t => Functor (Dynamic t) where
76 let e' = fmap f $ updated d
77 eb' = push (\b' -> liftM Just $ constant b') e'
78 b0 = fmap f $ current d
82mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
83mapDyn f = mapDynM $ return . f
85forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
89mapDynM :: 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)
90mapDynM f (~(Dynamic b e)) = do
91 let e' = push (liftM Just . f :: a -> PushM t (Maybe b)) e
92 eb' = fmap constant e'
93 v0 = pull $ f =<< sample b
94 bb' :: Behavior t (Behavior t b) <- hold v0 eb'
95 let b' = pull $ sample =<< sample bb'
96 return $ Dynamic b' e'
98foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
99foldDyn f = foldDynM (\o v -> return $ f o v)
101foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
103 rec let e' = flip push e $ \o -> do
107 return $ Dynamic b' e'
109count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
110count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
112toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
113toggle = foldDyn (const not)
115-- | Switches to the new event whenever it receives one. Switching occurs *before* occurring the inner event.
116switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
117switchPromptlyDyn de =
118 let eLag = switch $ current de
119 eCoincidences = coincidence $ updated de
120 in leftmost [eCoincidences, eLag]
124mergeEventsWith :: Reflex t m => (a -> a -> a) -> Event t a -> Event t a -> m (Event t a)
125mergeEventsWith f ea eb = mapE (mergeThese f) =<< alignEvents ea eb
127firstE :: (Reflex t m) => [Event t a] -> m (Event t a)
128firstE [] = return never
129firstE (h:t) = mergeEventsLeftBiased h =<< firstE t
131concatEventsWith :: (Reflex t m) => (a -> a -> a) -> [Event t a] -> m (Event t a)
132concatEventsWith _ [] = return never
133concatEventsWith _ [e] = return e
134concatEventsWith 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
135--concatEventsWith f (h:t) = mergeEventsWith f h =<< concatEventsWith f t
137mconcatE :: (Reflex t m, Monoid a) => [Event t a] -> m (Event t a)
138mconcatE = concatEventsWith mappend
141splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
142splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
144mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
146 ddm :: Dynamic t (DMap (Const2 Int a)) <- distributeDMapOverDyn $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
147 mapDyn (mconcat . map (\(Const2 _ :=> v) -> v) . DMap.toList) ddm
149distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap (WrapArg (Dynamic t) k) -> m (Dynamic t (DMap k))
150distributeDMapOverDyn dm = case DMap.toList dm of
151 [] -> return $ constDyn DMap.empty
152 [WrapArg k :=> v] -> mapDyn (DMap.singleton k) v
154 let edmPre = merge $ rewrapDMap updated dm
155 edm :: Event t (DMap k) = flip push edmPre $ \o -> return . Just =<< do
157 This origDyn -> sample $ current origDyn
158 That _ -> error "distributeDMapOverDyn: should be impossible to have an event occurring that is not present in the original DMap"
159 These _ (Identity newVal) -> return newVal
160 sequenceDmap $ combineDMapsWithKey f dm (wrapDMap Identity o)
161 dm0 :: Behavior t (DMap k) = pull $ do
162 liftM DMap.fromList $ forM (DMap.toList dm) $ \(WrapArg k :=> dv) -> liftM (k :=>) $ sample $ current dv
163 bbdm :: Behavior t (Behavior t (DMap k)) <- hold dm0 $ fmap constant edm
164 let bdm = pull $ sample =<< sample bbdm
165 return $ Dynamic bdm edm
167combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
168combineDyn f (~(Dynamic ba ea)) (~(Dynamic bb eb)) = do
169 let eab = align ea eb
170 ec = flip push eab $ \o -> do
178 These a b -> return (a, b)
179 return $ Just $ f a b
180 c0 :: Behavior t c = pull $ liftM2 f (sample ba) (sample bb)
181 bbc :: Behavior t (Behavior t c) <- hold c0 $ fmap constant ec
182 let bc :: Behavior t c = pull $ sample =<< sample bbc
183 return $ Dynamic bc ec
186tagInnerDyn :: Reflex t => Event t (Dynamic t a) -> Event t a
188 let eSlow = push (liftM Just . sample . current) e
189 eFast = coincidence $ fmap updated e
190 in leftmost [eFast, eSlow]
193joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
195 let b' = pull $ sample . current =<< sample (current dd)
196 eOuter :: Event t a = pushAlways (sample . current) $ updated dd
197 eInner :: Event t a = switch $ fmap updated (current dd)
198 eBoth :: Event t a = coincidence $ fmap updated (updated dd)
199 e' = leftmost [eBoth, eOuter, eInner]
202traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
203traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
205traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
207 let e' = traceEventWith f $ updated d
208 in Dynamic (current d) e'
210tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
211tagDyn = attachDynWith const
213attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
214attachDyn = attachDynWith (,)
216attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
217attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
219attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
220attachDynWithMaybe f d e =
221 let e' = attach (current d) e
222 in fforMaybe (align e' $ updated d) $ \case
223 This (a, b) -> f a b -- Only the tagging event is firing, so use that
224 These (_, b) a -> f a b -- Both events are firing, so use the newer value
225 That _ -> Nothing -- The tagging event isn't firing, so don't fire
227--------------------------------------------------------------------------------
229--------------------------------------------------------------------------------
231data Demux t k = Demux (Behavior t k) (EventSelector t (Const2 k Bool))
233demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
234demux 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))
236--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
237getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
238getDemuxed (Demux bk es) k = do
239 let e = select es (Const2 k)
240 bb <- hold (liftM (==k) $ sample bk) $ fmap return e
241 let b = pull $ join $ sample bb
244--------------------------------------------------------------------------------
246--------------------------------------------------------------------------------
248--TODO: This whole section is badly in need of cleanup
251 FHNil :: FHList f '[]
252 FHCons :: f e -> FHList f l -> FHList f (e ': l)
254natMap :: (forall a. f a -> g a) -> FHList f l -> FHList g l
257 h `FHCons` t -> f h `FHCons` natMap f t
259sequenceFHList :: Monad m => FHList m l -> m (HList l)
260sequenceFHList = \case
264 tResult <- sequenceFHList t
265 return $ hResult `HCons` tResult
267instance GEq (HListPtr l) where
268 HHeadPtr `geq` HHeadPtr = Just Refl
269 HHeadPtr `geq` HTailPtr _ = Nothing
270 HTailPtr _ `geq` HHeadPtr = Nothing
271 HTailPtr a `geq` HTailPtr b = a `geq` b
273instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
274 HHeadPtr `gcompare` HHeadPtr = GEQ
275 HHeadPtr `gcompare` HTailPtr _ = GLT
276 HTailPtr _ `gcompare` HHeadPtr = GGT
277 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
279data HListPtr l a where
280 HHeadPtr :: HListPtr (h ': t) h
281 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
283fhlistToDMap :: forall f l. FHList f l -> DMap (WrapArg f (HListPtr l))
284fhlistToDMap = DMap.fromList . go
285 where go :: forall l'. FHList f l' -> [DSum (WrapArg f (HListPtr l'))]
288 FHCons h t -> (WrapArg HHeadPtr :=> h) : map (\(WrapArg p :=> v) -> WrapArg (HTailPtr p) :=> v) (go t)
290class RebuildSortedHList l where
291 rebuildSortedFHList :: [DSum (WrapArg f (HListPtr l))] -> FHList f l
292 rebuildSortedHList :: [DSum (HListPtr l)] -> HList l
294instance RebuildSortedHList '[] where
295 rebuildSortedFHList [] = FHNil
296 rebuildSortedHList [] = HNil
298instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
299 rebuildSortedFHList ((WrapArg HHeadPtr :=> h) : t) = FHCons h $ rebuildSortedFHList $ map (\(WrapArg (HTailPtr p) :=> v) -> WrapArg p :=> v) t
300 rebuildSortedHList ((HHeadPtr :=> h) : t) = HCons h $ rebuildSortedHList $ map (\(HTailPtr p :=> v) -> p :=> v) t
303dmapToFHList :: forall f l. RebuildSortedHList l => DMap (WrapArg f (HListPtr l)) -> FHList f l
304dmapToFHList = rebuildSortedFHList . DMap.toList
306dmapToHList :: forall f l. RebuildSortedHList l => DMap (HListPtr l) -> HList l
307dmapToHList = rebuildSortedHList . DMap.toList
309distributeFHListOverDyn :: forall t m l. (Reflex t, MonadHold t m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
310distributeFHListOverDyn l = mapDyn dmapToHList =<< distributeDMapOverDyn (fhlistToDMap l)
312distributeFHListOverDyn l = do
314 c0 = pull $ sequenceFHList $ natMap (sample . current) l
315 bbc <- hold c0 $ fmap constant ec
316 let bc = pull $ sample =<< sample bbc
317 return $ Dynamic bc ec
320class AllAreFunctors (f :: a -> *) (l :: [a]) where
321 type FunctorList f l :: [*]
322 toFHList :: HList (FunctorList f l) -> FHList f l
323 fromFHList :: FHList f l -> HList (FunctorList f l)
325instance AllAreFunctors f '[] where
326 type FunctorList f '[] = '[]
327 toFHList HNil = FHNil
328 fromFHList FHNil = HNil
330instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
331 type FunctorList f (h ': t) = f h ': FunctorList f t
332 toFHList (a `HCons` b) = a `FHCons` toFHList b
333 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
335collectDyn :: ( RebuildSortedHList (HListElems b)
336 , IsHList a, IsHList b
337 , AllAreFunctors (Dynamic t) (HListElems b)
338 , Reflex t, MonadHold t m
339 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
340 ) => a -> m (Dynamic t b)
342 mapDyn fromHList =<< distributeFHListOverDyn (toFHList $ toHList ds)
346 type HListElems a :: [*]
347 toHList :: a -> HList (HListElems a)
348 fromHList :: HList (HListElems a) -> a
350instance IsHList (a, b) where
351 type HListElems (a, b) = [a, b]
352 toHList (a, b) = hBuild a b
353 fromHList (a `HCons` b `HCons` HNil) = (a, b)
355instance IsHList (a, b, c, d) where
356 type HListElems (a, b, c, d) = [a, b, c, d]
357 toHList (a, b, c, d) = hBuild a b c d
358 fromHList (a `HCons` b `HCons` c `HCons` d `HCons` HNil) = (a, b, c, d)
360instance IsHList (a, b, c, d, e, f) where
361 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
362 toHList (a, b, c, d, e, f) = hBuild a b c d e f
363 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