2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE NoMonomorphismRestriction #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE UndecidableInstances #-}
15-- | This module contains various functions for working with 'Dynamic' values.
16-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
19 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
31 , attachPromptlyDynWith
32 , attachPromptlyDynWithMaybe
41 , distributeDMapOverDynPure
45 -- Things that probably aren't very useful:
49 , distributeFHListOverDynPure
52 -- * Deprecated functions
59 , distributeDMapOverDyn
60 , distributeFHListOverDyn
71import Prelude hiding (mapM, mapM_)
73import Data.Functor.Misc
76import Control.Applicative ((<*>))
77import Control.Monad hiding (forM, forM_, mapM, mapM_)
78import Control.Monad.Fix
79import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
81import Data.Dependent.Map (DMap)
82import qualified Data.Dependent.Map as DMap
83import Data.Dependent.Sum (DSum (..))
84import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
91-- | Create a new 'Dynamic' that only signals changes if the values actually
93uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
94uniqDyn = uniqDynBy (==)
96-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
97-- changes and the given function returns 'False' when given both the old and
99uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
101 let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
102 in unsafeDynamic (current d) e'
104-- | Create a 'Dynamic' using the initial value and change it each time the
105-- 'Event' occurs using a folding function on the previous value and the value
107foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
108foldDyn = accum . flip
110-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
111-- can 'sample' existing 'Behaviors' and 'hold' new ones.
112foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
113foldDynM = accumM . flip
115-- | Create a 'Dynamic' using the provided initial value and change it each time
116-- the provided 'Event' occurs, using a function to combine the old value with
117-- the 'Event''s value. If the function returns 'Nothing', the value is not
118-- changed; this is distinct from returning 'Just' the old value, since the
119-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
120-- in the 'Nothing' case.
121foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
122foldDynMaybe = accumMaybe . flip
124-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
125-- can 'sample' existing 'Behaviors' and 'hold' new ones.
126foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
127foldDynMaybeM = accumMaybeM . flip
129-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
130count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
131count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
133-- | Create a new 'Dynamic' using the initial value that flips its
134-- value every time the 'Event' occurs.
135toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
136toggle = foldDyn (const not)
138-- | Switches to the new 'Event' whenever it receives one. Switching occurs
139-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
140-- old and new inner Events fire simultaneously, the output will fire with the
141-- value of the *new* 'Event'.
142switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
143switchPromptlyDyn de =
144 let eLag = switch $ current de
145 eCoincidences = coincidence $ updated de
146 in leftmost [eCoincidences, eLag]
148-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
149splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
150splitDynPure d = (fmap fst d, fmap snd d)
152-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of 'Dynamic'
153-- values. The resulting 'Dynamic' will change whenever one of the input
154-- 'Dynamic's changes.
156-- This function's implementation is more efficient than simply using
157-- 'zipDynWith' or applicative syntax to achieve the same result, especially for
158-- maps with many elements.
159distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
160distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
162--TODO: Generalize this to functors other than Maps
163-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
164-- with the current values of the 'Dynamic's in a map.
165joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
166joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
168-- | Print the value of the 'Dynamic' on each change and prefix it
169-- with the provided string. This should /only/ be used for debugging.
171-- Note: Just like Debug.Trace.trace, the value will only be shown if something
172-- else in the system is depending on it.
173traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
174traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
176-- | Print the result of applying the provided function to the value
177-- of the 'Dynamic' on each change. This should /only/ be used for
180-- Note: Just like Debug.Trace.trace, the value will only be shown if something
181-- else in the system is depending on it.
182traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
184 let e' = traceEventWith f $ updated d
186 x <- sample $ current d
187 trace (f x) $ return x
188 in unsafeBuildDynamic getV0 e'
190-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
191-- each time the 'Event' occurs.
193-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
194-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
195-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
196-- will be used, since the 'Behavior' won't be updated until the end of the frame.
197-- Additionally, this means that the output 'Event' may not be used to directly change
198-- the input 'Dynamic', because that would mean its value depends on itself. When creating
199-- cyclic data flows, generally `tag (current d) e` is preferred.
200tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
201tagPromptlyDyn = attachPromptlyDynWith const
203-- | Attach the current value of the 'Dynamic' to the value of the
204-- 'Event' each time it occurs.
206-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
207attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
208attachPromptlyDyn = attachPromptlyDynWith (,)
210-- | Combine the current value of the 'Dynamic' with the value of the
211-- 'Event' each time it occurs.
213-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
214attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
215attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
217-- | Create a new 'Event' by combining the value at each occurence with the
218-- current value of the 'Dynamic' value and possibly filtering if the combining
219-- function returns 'Nothing'.
221-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
222-- (current d)`. See 'tagPromptlyDyn' for details.
223attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
224attachPromptlyDynWithMaybe f d e =
225 let e' = attach (current d) e
226 in fforMaybe (align e' $ updated d) $ \case
227 This (a, b) -> f a b -- Only the tagging event is firing, so use that
228 These (_, b) a -> f a b -- Both events are firing, so use the newer value
229 That _ -> Nothing -- The tagging event isn't firing, so don't fire
231--------------------------------------------------------------------------------
233--------------------------------------------------------------------------------
235-- | Represents a time changing value together with an 'EventSelector' that can
236-- efficiently detect when the underlying 'Dynamic' has a particular value.
237-- This is useful for representing data like the current selection of a long
242-- > demuxed (demux d) k === fmap (== k) d
244-- However, the when getDemuxed is used multiple times, the complexity is only
245-- /O(log(n))/, rather than /O(n)/ for fmap.
246data Demux t k = Demux { demuxValue :: Behavior t k
247 , demuxSelector :: EventSelector t (Const2 k Bool)
250-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
251-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
252demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
253demux k = Demux (current k)
254 (fan $ attachWith (\k0 k1 -> if k0 == k1
256 else DMap.fromList [Const2 k0 :=> Identity False,
257 Const2 k1 :=> Identity True])
258 (current k) (updated k))
260-- | Select a particular output of the 'Demux'; this is equivalent to (but much
261-- faster than) mapping over the original 'Dynamic' and checking whether it is
262-- equal to the given key.
263demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
265 let e = select (demuxSelector d) (Const2 k)
266 in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
268--------------------------------------------------------------------------------
270--------------------------------------------------------------------------------
272--TODO: This whole section is badly in need of cleanup
274-- | A heterogeneous list whose type and length are fixed statically. This is
275-- reproduced from the 'HList' package due to integration issues, and because
276-- very little other functionality from that library is needed.
277data HList (l::[*]) where
279 HCons :: e -> HList l -> HList (e ': l)
283type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
284type instance HRevApp '[] l = l
285type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
287hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
289hRevApp (HCons x l) l' = hRevApp l (HCons x l')
291hReverse :: HList l -> HList (HRevApp l '[])
292hReverse l = hRevApp l HNil
294hBuild :: (HBuild' '[] r) => r
297class HBuild' l r where
298 hBuild' :: HList l -> r
300instance (l' ~ HRevApp l '[])
301 => HBuild' l (HList l') where
304instance HBuild' (a ': l) r
305 => HBuild' l (a->r) where
306 hBuild' l x = hBuild' (HCons x l)
308-- | Like 'HList', but with a functor wrapping each element.
310 FHNil :: FHList f '[]
311 FHCons :: f e -> FHList f l -> FHList f (e ': l)
313instance GEq (HListPtr l) where
314 HHeadPtr `geq` HHeadPtr = Just Refl
315 HHeadPtr `geq` HTailPtr _ = Nothing
316 HTailPtr _ `geq` HHeadPtr = Nothing
317 HTailPtr a `geq` HTailPtr b = a `geq` b
319instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
320 HHeadPtr `gcompare` HHeadPtr = GEQ
321 HHeadPtr `gcompare` HTailPtr _ = GLT
322 HTailPtr _ `gcompare` HHeadPtr = GGT
323 HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
325data HListPtr l a where
326 HHeadPtr :: HListPtr (h ': t) h
327 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
329fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
330fhlistToDMap = DMap.fromList . go
331 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
334 FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
336class RebuildSortedHList l where
337 rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
338 rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
340instance RebuildSortedHList '[] where
341 rebuildSortedFHList l = case l of
343 _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
344 rebuildSortedHList l = case l of
346 _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
348instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
349 rebuildSortedFHList l = case l of
350 ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
351 _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
352 rebuildSortedHList l = case l of
353 ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
354 _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
356dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
357dmapToHList = rebuildSortedHList . DMap.toList
359-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
360-- single 'Dynamic' whose value represents the current values of all of the
362distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
363distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
365class AllAreFunctors (f :: a -> *) (l :: [a]) where
366 type FunctorList f l :: [*]
367 toFHList :: HList (FunctorList f l) -> FHList f l
368 fromFHList :: FHList f l -> HList (FunctorList f l)
370instance AllAreFunctors f '[] where
371 type FunctorList f '[] = '[]
372 toFHList l = case l of
374#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
375 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
377 fromFHList FHNil = HNil
379instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
380 type FunctorList f (h ': t) = f h ': FunctorList f t
381 toFHList l = case l of
382 a `HCons` b -> a `FHCons` toFHList b
383#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
384 _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
386 fromFHList (a `FHCons` b) = a `HCons` fromFHList b
388-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
389-- single 'Dynamic' whose value represents all the current values of the input's
390-- consitutent 'Dynamic's.
391collectDynPure :: ( RebuildSortedHList (HListElems b)
392 , IsHList a, IsHList b
393 , AllAreFunctors (Dynamic t) (HListElems b)
395 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
396 ) => a -> Dynamic t b
397collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
401 type HListElems a :: [*]
402 toHList :: a -> HList (HListElems a)
403 fromHList :: HList (HListElems a) -> a
405instance IsHList (a, b) where
406 type HListElems (a, b) = [a, b]
407 toHList (a, b) = hBuild a b
408 fromHList l = case l of
409 a `HCons` b `HCons` HNil -> (a, b)
410#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
411 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
414instance IsHList (a, b, c, d) where
415 type HListElems (a, b, c, d) = [a, b, c, d]
416 toHList (a, b, c, d) = hBuild a b c d
417 fromHList l = case l of
418 a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
419#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
420 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
423instance IsHList (a, b, c, d, e, f) where
424 type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
425 toHList (a, b, c, d, e, f) = hBuild a b c d e f
426 fromHList l = case l of
427 a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
428#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
429 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
432--------------------------------------------------------------------------------
433-- Deprecated functions
434--------------------------------------------------------------------------------
436-- | Map a function over a 'Dynamic'.
437{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
438mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
439mapDyn f = return . fmap f
441-- | Flipped version of 'mapDyn'.
442{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
443forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
444forDyn a = return . ffor a
446-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
448{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
449splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
450splitDyn = return . splitDynPure
452-- | Merge the 'Dynamic' values using their 'Monoid' instance.
453{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
454mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
455mconcatDyn = return . mconcat
457-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
458{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
459distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
460distributeDMapOverDyn = return . distributeDMapOverDynPure
462-- | Merge two 'Dynamic's into a new one using the provided function. The new
463-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
465{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
466combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
467combineDyn f a b = return $ zipDynWith f a b
469-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
472-- > person <- Person `mapDyn` dynFirstName
473-- > `apDyn` dynListName
475-- > `apDyn` dynAddress
476{-# 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" #-}
477{-# ANN apDyn "HLint: ignore Use fmap" #-}
478apDyn :: forall t m a b. (Reflex t, Monad m)
479 => m (Dynamic t (a -> b))
482apDyn m a = liftM (<*> a) m
484--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
485-- | This function no longer needs to be monadic, so it has been replaced by
486-- 'demuxed', which is pure.
487{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
488getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
489getDemuxed d = return . demuxed d
491-- | This function no longer needs to be monadic, so it has been replaced by
492-- 'distributeFHListOverDynPure', which is pure.
493{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
494distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
495distributeFHListOverDyn = return . distributeFHListOverDynPure
497-- | This function no longer needs to be monadic, so it has been replaced by
498-- 'collectDynPure', which is pure.
499{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
500collectDyn :: ( RebuildSortedHList (HListElems b)
501 , IsHList a, IsHList b
502 , AllAreFunctors (Dynamic t) (HListElems b)
504 , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
505 ) => a -> m (Dynamic t b)
506collectDyn = return . collectDynPure
508-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
510{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
511tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
512tagDyn = tagPromptlyDyn
514-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
516{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
517attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
518attachDyn = attachPromptlyDyn
520-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
522{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
523attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
524attachDynWith = attachPromptlyDynWith
526-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
528{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
529attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
530attachDynWithMaybe = attachPromptlyDynWithMaybe
532-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
533-- current value will always be equal to the current value's current value, and
534-- will change whenever either the inner or the outer (or both) values change.
535{-# DEPRECATED joinDyn "Use 'join' instead" #-}
536joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
539-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
540-- been renamed to 'uniqDyn'
541{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
542nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
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