on develop help sign in
Merge branch 'cocreature-docs-dynamic2' into develop
7dbb590Ryan Trinkle
src/Reflex/Dynamic.hs+83−2
@@ -88,27 +88,36 @@ instance HBuild' (a ': l) r
88 => HBuild' l (a->r) where
88 => HBuild' l (a->r) where
89 hBuild' l x = hBuild' (HCons x l)
89 hBuild' l x = hBuild' (HCons x l)
90
90
91−
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', with a rule that the Behavior will
93+-- change if and only if the Event fires.
92data Dynamic t a
94data Dynamic t a
93 = Dynamic (Behavior t a) (Event t a)
95 = Dynamic (Behavior t a) (Event t a)
94
96
95unsafeDynamic :: Behavior t a -> Event t a -> Dynamic t a
97unsafeDynamic :: Behavior t a -> Event t a -> Dynamic t a
96unsafeDynamic = Dynamic
98unsafeDynamic = Dynamic
97
99
100+-- | Extract the 'Behavior' of a 'Dynamic'.
98current :: Dynamic t a -> Behavior t a
101current :: Dynamic t a -> Behavior t a
99current (Dynamic b _) = b
102current (Dynamic b _) = b
100
103
104+-- | Extract the 'Event' of the 'Dynamic'.
101updated :: Dynamic t a -> Event t a
105updated :: Dynamic t a -> Event t a
102updated (Dynamic _ e) = e
106updated (Dynamic _ e) = e
103
107
108+-- | 'Dynamic' with the constant supplied value.
104constDyn :: Reflex t => a -> Dynamic t a
109constDyn :: Reflex t => a -> Dynamic t a
105constDyn x = Dynamic (constant x) never
110constDyn x = Dynamic (constant x) never
106
111
112+-- | Create a 'Dynamic' using the initial value that changes every
113+-- time the 'Event' occurs.
107holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
114holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
108holdDyn v0 e = do
115holdDyn v0 e = do
109 b <- hold v0 e
116 b <- hold v0 e
110 return $ Dynamic b e
117 return $ Dynamic b e
111
118
119+-- | Create a new 'Dynamic' that only signals changes if the values
120+-- actually changed.
112nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
121nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
113nubDyn d =
122nubDyn d =
114 let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
123 let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
@@ -123,12 +132,16 @@ instance Reflex t => Functor (Dynamic t) where
123
132
124-}
133-}
125
134
135+-- | Map a function over a 'Dynamic'.
126mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
136mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
127mapDyn f = mapDynM $ return . f
137mapDyn f = mapDynM $ return . f
128
138
139+-- | Flipped version of 'mapDyn'.
129forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
140forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
130forDyn = flip mapDyn
141forDyn = flip mapDyn
131
142
143+-- | Map a monadic function over a 'Dynamic'. The only monadic action that the given function can
144+-- perform is 'sample'.
132{-# INLINE mapDynM #-}
145{-# INLINE mapDynM #-}
133mapDynM :: 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)
146mapDynM :: 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)
134mapDynM f d = do
147mapDynM f d = do
@@ -139,9 +152,15 @@ mapDynM f d = do
139 let b' = pull $ sample =<< sample bb'
152 let b' = pull $ sample =<< sample bb'
140 return $ Dynamic b' e'
153 return $ Dynamic b' e'
141
154
155+-- | Create a 'Dynamic' using the initial value and change it each
156+-- time the 'Event' occurs using a folding function on the previous
157+-- value and the value of the 'Event'.
142foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
158foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
143foldDyn f = foldDynM (\o v -> return $ f o v)
159foldDyn f = foldDynM (\o v -> return $ f o v)
144
160
161+-- | Create a 'Dynamic' using the initial value and change it each
162+-- time the 'Event' occurs using a monadic folding function on the
163+-- previous value and the value of the 'Event'.
145foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
164foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
146foldDynM f z e = do
165foldDynM f z e = do
147 rec let e' = flip push e $ \o -> do
166 rec let e' = flip push e $ \o -> do
@@ -150,13 +169,18 @@ foldDynM f z e = do
150 b' <- hold z e'
169 b' <- hold z e'
151 return $ Dynamic b' e'
170 return $ Dynamic b' e'
152
171
172+-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
153count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
173count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
154count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
174count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
155
175
176+-- | Create a new 'Dynamic' using the initial value that flips its
177+-- value every time the 'Event' occurs.
156toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
178toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
157toggle = foldDyn (const not)
179toggle = foldDyn (const not)
158
180
159−-- | Switches to the new event whenever it receives one. Switching occurs *before* occurring the inner event.
181+-- | Switches to the new 'Event' whenever it receives one. Switching
182+-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
183+-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
160switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
184switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
161switchPromptlyDyn de =
185switchPromptlyDyn de =
162 let eLag = switch $ current de
186 let eLag = switch $ current de
@@ -182,14 +206,19 @@ mconcatE :: (Reflex t m, Monoid a) => [Event t a] -> m (Event t a)
182mconcatE = concatEventsWith mappend
206mconcatE = concatEventsWith mappend
183-}
207-}
184
208
209+-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
210+-- respective value of the tuple.
185splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
211splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
186splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
212splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
187
213
214+-- | Merge the 'Dynamic' values using their 'Monoid' instance.
188mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
215mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
189mconcatDyn es = do
216mconcatDyn es = do
190 ddm :: Dynamic t (DMap (Const2 Int a)) <- distributeDMapOverDyn $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
217 ddm :: Dynamic t (DMap (Const2 Int a)) <- distributeDMapOverDyn $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
191 mapDyn (mconcat . map (\(Const2 _ :=> v) -> v) . DMap.toList) ddm
218 mapDyn (mconcat . map (\(Const2 _ :=> v) -> v) . DMap.toList) ddm
192
219
220+-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of
221+-- Dynamic values.
193distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap (WrapArg (Dynamic t) k) -> m (Dynamic t (DMap k))
222distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap (WrapArg (Dynamic t) k) -> m (Dynamic t (DMap k))
194distributeDMapOverDyn dm = case DMap.toList dm of
223distributeDMapOverDyn dm = case DMap.toList dm of
195 [] -> return $ constDyn DMap.empty
224 [] -> return $ constDyn DMap.empty
@@ -208,6 +237,9 @@ distributeDMapOverDyn dm = case DMap.toList dm of
208 let bdm = pull $ sample =<< sample bbdm
237 let bdm = pull $ sample =<< sample bbdm
209 return $ Dynamic bdm edm
238 return $ Dynamic bdm edm
210
239
240+-- | Merge two 'Dynamic's into a new one using the provided
241+-- function. The new 'Dynamic' changes its value each time one of the
242+-- original 'Dynamic's changes its value.
211combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
243combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
212combineDyn f da db = do
244combineDyn f da db = do
213 let eab = align (updated da) (updated db)
245 let eab = align (updated da) (updated db)
@@ -234,6 +266,8 @@ tagInnerDyn e =
234 in leftmost [eFast, eSlow]
266 in leftmost [eFast, eSlow]
235-}
267-}
236
268
269+-- | Join a nested 'Dynamic' into a new 'Dynamic' that has the value
270+-- of the inner 'Dynamic'.
237joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
271joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
238joinDyn dd =
272joinDyn dd =
239 let b' = pull $ sample . current =<< sample (current dd)
273 let b' = pull $ sample . current =<< sample (current dd)
@@ -244,6 +278,8 @@ joinDyn dd =
244 in Dynamic b' e'
278 in Dynamic b' e'
245
279
246--TODO: Generalize this to functors other than Maps
280--TODO: Generalize this to functors other than Maps
281+-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
282+-- with the current values of the 'Dynamic's in a map.
247joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
283joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
248joinDynThroughMap dd =
284joinDynThroughMap dd =
249 let b' = pull $ mapM (sample . current) =<< sample (current dd)
285 let b' = pull $ mapM (sample . current) =<< sample (current dd)
@@ -258,23 +294,57 @@ joinDynThroughMap dd =
258 e' = leftmost [eBoth, eOuter, eInner]
294 e' = leftmost [eBoth, eOuter, eInner]
259 in Dynamic b' e'
295 in Dynamic b' e'
260
296
297+-- | Print the value of the 'Dynamic' on each change and prefix it
298+-- with the provided string. This should /only/ be used for debugging.
299+--
300+-- Note: Just like Debug.Trace.trace, the value will only be shown if something
301+-- else in the system is depending on it.
261traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
302traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
262traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
303traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
263
304
305+-- | Print the result of applying the provided function to the value
306+-- of the 'Dynamic' on each change. This should /only/ be used for
307+-- debugging.
308+--
309+-- Note: Just like Debug.Trace.trace, the value will only be shown if something
310+-- else in the system is depending on it.
264traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
311traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
265traceDynWith f d =
312traceDynWith f d =
266 let e' = traceEventWith f $ updated d
313 let e' = traceEventWith f $ updated d
267 in Dynamic (current d) e'
314 in Dynamic (current d) e'
268
315
316+-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
317+-- each time the 'Event' occurs.
318+--
319+-- Note: `tagDyn d e` differs from `tag (current d) e` in the case that `e` is firing
320+-- at the same time that `d` is changing. With `tagDyn d e`, the *new* value of `d`
321+-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
322+-- will be used, since the 'Behavior' won't be updated until the end of the frame.
323+-- Additionally, this means that the output 'Event' may not be used to directly change
324+-- the input 'Dynamic', because that would mean its value depends on itself. When creating
325+-- cyclic data flows, generally `tag (current d) e` is preferred.
269tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
326tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
270tagDyn = attachDynWith const
327tagDyn = attachDynWith const
271
328
329+-- | Attach the current value of the 'Dynamic' to the value of the
330+-- 'Event' each time it occurs.
331+--
332+-- Note: `attachDyn d` is not the same as `attach (current d)`. See 'tagDyn' for details.
272attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
333attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
273attachDyn = attachDynWith (,)
334attachDyn = attachDynWith (,)
274
335
336+-- | Combine the current value of the 'Dynamic' with the value of the
337+-- 'Event' each time it occurs.
338+--
339+-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`. See 'tagDyn' for details.
275attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
340attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
276attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
341attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
277
342
343+-- | Create a new 'Event' by combining the value at each occurence
344+-- with the current value of the 'Dynamic' value and possibly
345+-- filtering if the combining function returns 'Nothing'.
346+--
347+-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`. See 'tagDyn' for details.
278attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
348attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
279attachDynWithMaybe f d e =
349attachDynWithMaybe f d e =
280 let e' = attach (current d) e
350 let e' = attach (current d) e
@@ -287,14 +357,25 @@ attachDynWithMaybe f d e =
287-- Demux
357-- Demux
288--------------------------------------------------------------------------------
358--------------------------------------------------------------------------------
289
359
360+-- | Represents a time changing value together with an 'EventSelector'
361+-- that can efficiently detect when the underlying Dynamic has a particular value.
362+-- This is useful for representing data like the current selection of a long list.
363+--
364+-- Semantically,
365+-- > getDemuxed (demux d) k === mapDyn (== k) d
366+-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
367+-- rather than /O(n)/ for mapDyn.
290data Demux t k = Demux { demuxValue :: Behavior t k
368data Demux t k = Demux { demuxValue :: Behavior t k
291 , demuxSelector :: EventSelector t (Const2 k Bool)
369 , demuxSelector :: EventSelector t (Const2 k Bool)
292 }
370 }
293
371
372+-- | Demultiplex an input value to a 'Demux' with many outputs. At any given time, whichever output is indicated by the given 'Dynamic' will be 'True'.
294demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
373demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
295demux 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))
374demux 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))
296
375
297--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
376--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
377+-- | Select a particular output of the 'Demux'; this is equivalent to (but much faster than)
378+-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
298getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
379getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
299getDemuxed d k = do
380getDemuxed d k = do
300 let e = select (demuxSelector d) (Const2 k)
381 let e = select (demuxSelector d) (Const2 k)