reflex 0.5.0.1 → 0.6
raw patch · 31 files changed
+666/−466 lines, 31 filesdep +witherabledep ~containersdep ~mtlnew-uploader
Dependencies added: witherable
Dependency ranges changed: containers, mtl
Files
- ChangeLog.md +12/−0
- Quickref.md +7/−7
- README.md +10/−5
- reflex.cabal +7/−3
- src/Data/AppendMap.hs +6/−16
- src/Data/Functor/Misc.hs +1/−44
- src/Reflex.hs +1/−0
- src/Reflex/Class.hs +219/−79
- src/Reflex/Collection.hs +8/−10
- src/Reflex/Dynamic.hs +10/−158
- src/Reflex/Dynamic/TH.hs +1/−25
- src/Reflex/DynamicWriter/Base.hs +8/−3
- src/Reflex/DynamicWriter/Class.hs +9/−4
- src/Reflex/EventWriter/Base.hs +2/−2
- src/Reflex/FastWeak.hs +10/−6
- src/Reflex/FunctorMaybe.hs +23/−21
- src/Reflex/Network.hs +10/−4
- src/Reflex/NotReady/Class.hs +10/−0
- src/Reflex/Optimizer.hs +8/−4
- src/Reflex/Patch.hs +12/−5
- src/Reflex/Patch/Class.hs +1/−1
- src/Reflex/Profiled.hs +6/−0
- src/Reflex/Pure.hs +7/−5
- src/Reflex/Query/Base.hs +23/−30
- src/Reflex/Query/Class.hs +1/−1
- src/Reflex/Requester/Base.hs +107/−3
- src/Reflex/Spider.hs +5/−2
- src/Reflex/Spider/Internal.hs +5/−5
- src/Reflex/Time.hs +113/−13
- src/Reflex/Workflow.hs +13/−1
- test/RequesterT.hs +11/−9
+ ChangeLog.md view
@@ -0,0 +1,12 @@+# Revision history for reflex++## 0.6.0.0 -- 2019-03-20++* Deprecate FunctorMaybe in favor of Data.Witherable.Filterable. We still export fmapMaybe, ffilter, etc., but they all rely on Filterable now.+* Rename MonadDynamicWriter to DynamicWriter and add a deprecation for the old name.+* Remove many deprecated functions.+* Add a Num instance for Dynamic.+* Add matchRequestsWithResponses to make it easier to use Requester with protocols that don't do this matching for you.+* Add withRequesterT to map functions over the request and response of a RequesterT.+* Suppress nil patches in QueryT as an optimization. The Query type must now have an Eq instance.+* Add throttleBatchWithLag to Reflex.Time. See that module for details.
Quickref.md view
@@ -57,15 +57,15 @@ [ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a [ ] leftmost :: [Event a] -> Event a [ ] mergeList :: [Event a] -> Event (NonEmpty a)-[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)+[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity) [ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a) -- Efficient one-to-many fanout-[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)-[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k-[ ] select :: EventSelector k -> k a -> Event a-[ ] fanEither :: Event (Either a b) -> (Event a, Event b)-[ ] fanThese :: Event (These a b) -> (Event a, Event b)+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)+[ ] fan :: GCompare k => Event (DMap k Identity) -> EventSelector k+[ ] select :: EventSelector k -> k a -> Event a+[ ] fanEither :: Event (Either a b) -> (Event a, Event b)+[ ] fanThese :: Event (These a b) -> (Event a, Event b) -- Event to Event via function that can sample current values [ ] push :: (a -> m (Maybe b)) -> Event a -> Event b@@ -166,7 +166,7 @@ -- Flatten Behavior-of-Event to Event. Old Event is used during switchover. [ ] switch :: Behavior (Event a) -> Event a --- Flatten Dyanmic-of-Event to Event. New Event is used immediately.+-- Flatten Dynamic-of-Event to Event. New Event is used immediately. [ ] switchDyn :: Dynamic (Event a) -> Event a -- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
README.md view
@@ -1,15 +1,15 @@-## Reflex+## [Reflex](https://reflex-frp.org/) ### Practical Functional Reactive Programming -Reflex is an fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.+Reflex is a fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface. [Reflex-DOM](https://github.com/reflex-frp/reflex-dom) is a framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps. -Comprehensive documentation is still a work in progress, but a tutorial for Reflex and Reflex-DOM is available at https://github.com/reflex-frp/reflex-platform and an introductory talk given at the New York Haskell Meetup is available here: [Part 1](https://www.youtube.com/watch?v=mYvkcskJbc4) / [Part 2](https://www.youtube.com/watch?v=3qfc9XFVo2c).- A summary of Reflex functions is available in the [quick reference](Quickref.md). -### Additional resources+**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**++### Resources [Get started with Reflex](https://github.com/reflex-frp/reflex-platform) [/r/reflexfrp](https://www.reddit.com/r/reflexfrp)@@ -17,3 +17,8 @@ [hackage](https://hackage.haskell.org/package/reflex) [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)++### Hacking++Use the `./scripts/hack-on reflex` script in [Reflex Platform](https://github.com/reflex-frp/reflex-platform) to checkout the source code of `reflex` locally in `reflex-platform/reflex` directory.+Then do modifications to the source in place, and use the `./try-reflex` or `./scripts/work-on` scripts to create the shell to test your changes.
reflex.cabal view
@@ -1,5 +1,5 @@ Name: reflex-Version: 0.5.0.1+Version: 0.6 Synopsis: Higher-order Functional Reactive Programming Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system License: BSD3@@ -15,6 +15,7 @@ extra-source-files: README.md Quickref.md+ ChangeLog.md flag use-reflex-optimizer description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.@@ -64,7 +65,8 @@ time >= 1.4 && < 1.9, transformers >= 0.2, transformers-compat >= 0.3,- unbounded-delays >= 0.1.0.0 && < 0.2+ unbounded-delays >= 0.1.0.0 && < 0.2,+ witherable >= 0.2 && < 0.4 exposed-modules: Data.AppendMap,@@ -233,14 +235,16 @@ main-is: RequesterT.hs hs-source-dirs: test build-depends: base+ , containers+ , deepseq >= 1.3 && < 1.5 , dependent-sum , dependent-map , lens+ , mtl , these , transformers , reflex , ref-tf- buildable: False other-modules: Reflex.TestPlan Reflex.Plan.Pure
src/Data/AppendMap.hs view
@@ -1,15 +1,10 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- | 'Data.Map' with a better 'Monoid' instance@@ -31,9 +26,9 @@ #else import qualified Data.Map as Map (showTree, showTreeWith) #endif-import Data.Map.Monoidal-import Reflex.Class (FunctorMaybe (..))-import Reflex.Patch (Additive, Group (..))+import Data.Witherable (Filterable(..))+import Data.Map.Monoidal (MonoidalMap(..), delete, null, empty)+import qualified Data.Map.Monoidal as M {-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-} type AppendMap = MonoidalMap@@ -45,8 +40,8 @@ pattern AppendMap :: Map k v -> MonoidalMap k v pattern AppendMap m = MonoidalMap m -instance FunctorMaybe (MonoidalMap k) where- fmapMaybe = mapMaybe+instance Filterable (MonoidalMap k) where+ mapMaybe = M.mapMaybe -- | Deletes a key, returning 'Nothing' if the result is empty. nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)@@ -60,17 +55,12 @@ -> MonoidalMap token a -> Maybe (MonoidalMap token b) mapMaybeNoNull f as =- let bs = fmapMaybe f as+ let bs = mapMaybe f as in if null bs then Nothing else Just bs -- TODO: Move instances to `Reflex.Patch`-instance (Ord k, Group q) => Group (MonoidalMap k q) where- negateG = map negateG--instance (Ord k, Additive q) => Additive (MonoidalMap k q)- showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String showTree = coerce (Map.showTree :: Map k a -> String)
src/Data/Functor/Misc.hs view
@@ -38,17 +38,10 @@ , dmapToThese , eitherToDSum , dsumToEither- -- * Deprecated functions- , sequenceDmap- , wrapDMap- , rewrapDMap- , unwrapDMap- , unwrapDMapMaybe- , extractFunctorDMap , ComposeMaybe (..) ) where -import Control.Applicative (Applicative, (<$>))+import Control.Applicative ((<$>)) import Control.Monad.Identity import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap@@ -249,39 +242,3 @@ ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord) deriving instance Functor f => Functor (ComposeMaybe f)------------------------------------------------------------------------------------- Deprecated functions-----------------------------------------------------------------------------------{-# INLINE sequenceDmap #-}-{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ -> fmap Identity)' instead" #-}--- | Run the actions contained in the 'DMap'-sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)-sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t--{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}--- | Replace the 'Identity' functor for a 'DMap''s values with a different functor-wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f-wrapDMap f = DMap.map $ f . runIdentity--{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}--- | Replace one functor for a 'DMap''s values with a different functor-rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g-rewrapDMap = DMap.map--{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}--- | Replace one functor for a 'DMap''s values with the 'Identity' functor-unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity-unwrapDMap f = DMap.map $ Identity . f--{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}--- | Like 'unwrapDMap', but possibly delete some values from the DMap-unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity-unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a--{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}--- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the--- underlying functor-extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f-extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
src/Reflex.hs view
@@ -19,6 +19,7 @@ import Reflex.Dynamic.Uniq as X import Reflex.DynamicWriter.Base as X import Reflex.DynamicWriter.Class as X+import Reflex.NotReady.Class as X import Reflex.PerformEvent.Base as X import Reflex.PerformEvent.Class as X import Reflex.PostBuild.Base as X
src/Reflex/Class.hs view
@@ -21,9 +21,13 @@ {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} #endif --- | This module contains the Reflex interface, as well as a variety of--- convenience functions for working with 'Event's, 'Behavior's, and other--- signals.+-- |+-- Module:+-- Reflex.Class+-- Description:+-- This module contains the Reflex interface, as well as a variety of+-- convenience functions for working with 'Event's, 'Behavior's, and other+-- signals. module Reflex.Class ( module Reflex.Patch -- * Primitives@@ -133,8 +137,10 @@ -- * Unsafe functions , unsafeDynamic , unsafeMapIncremental- -- * 'FunctorMaybe'- , FunctorMaybe (..)+ -- * 'Filterable' convenience functions+ , FunctorMaybe -- fmapMaybe is purposely not exported from deprecated 'FunctorMaybe' and the new alias is exported instead+ , mapMaybe -- Re-exported for convenience+ , fmapMaybe , fforMaybe , ffilter , filterLeft@@ -144,10 +150,11 @@ , ffor2 , ffor3 -- * Deprecated functions- , appendEvents- , onceE- , sequenceThese+ , switchPromptly+ , switchPromptOnly+ -- * "Cheap" functions , fmapMaybeCheap+ , mapMaybeCheap , fmapCheap , fforCheap , fforMaybeCheap@@ -155,8 +162,6 @@ , tagCheap , mergeWithCheap , mergeWithCheap'- , switchPromptly- , switchPromptOnly -- * Slow, but general, implementations , slowHeadE ) where@@ -193,7 +198,10 @@ import Data.String import Data.These import Data.Type.Coercion-import Reflex.FunctorMaybe+import Data.Witherable (Filterable(..))+import qualified Data.Witherable as W+import Reflex.FunctorMaybe (FunctorMaybe)+import qualified Reflex.FunctorMaybe import Reflex.Patch import qualified Reflex.Patch.MapWithMove as PatchMapWithMove @@ -256,8 +264,7 @@ -- | Create an 'Event' that will occur whenever the currently-selected input -- 'Event' occurs switch :: Behavior t (Event t a) -> Event t a- -- | Create an 'Event' that will occur whenever the input event is occurring- -- and its occurrence value, another 'Event', is also occurring+ -- | Create an 'Event' that will occur whenever the input event is occurring -- and its occurrence value, another 'Event', is also occurring coincidence :: Event t (Event t a) -> Event t a -- | Extract the 'Behavior' of a 'Dynamic'. current :: Dynamic t a -> Behavior t a@@ -388,8 +395,8 @@ return $ case result of (Nothing, Nothing) -> Nothing _ -> Just result- d' <- holdIncremental z $ fmapMaybe fst e'- return (d', fmapMaybe snd e')+ d' <- holdIncremental z $ mapMaybe fst e'+ return (d', mapMaybe snd e') slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a) slowHeadE e = do@@ -399,14 +406,14 @@ -- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a -- key. This is much more efficient than filtering for each key with--- 'fmapMaybe'.+-- 'mapMaybe'. newtype EventSelector t k = EventSelector { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is -- determined by the type of the key, so this can be used to fan-out -- 'Event's whose sub-'Event's have different types. -- -- Using 'EventSelector's and the 'fan' primitive is far more efficient than- -- (but equivalent to) using 'fmapMaybe' to select only the relevant+ -- (but equivalent to) using 'mapMaybe' to select only the relevant -- occurrences of an 'Event'. select :: forall a. k a -> Event t a }@@ -541,6 +548,15 @@ negate = fmap negate signum = fmap signum +instance (Num a, Reflex t) => Num (Dynamic t a) where+ (+) = liftA2 (+)+ (*) = liftA2 (*)+ abs = fmap abs+ signum = fmap signum+ fromInteger = pure . fromInteger+ negate = fmap negate+ (-) = liftA2 (-)+ instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where a <> b = pull $ liftM2 (<>) (sample a) (sample b) sconcat = pull . fmap sconcat . mapM sample@@ -550,22 +566,25 @@ times1p n = fmap $ times1p n #endif --- | Flipped version of 'fmapMaybe'.-fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b-fforMaybe = flip fmapMaybe+-- | Alias for 'mapMaybe'+fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b+fmapMaybe = mapMaybe +-- | Flipped version of 'mapMaybe'.+fforMaybe :: Filterable f => f a -> (a -> Maybe b) -> f b+fforMaybe = flip mapMaybe+ -- | Filter 'f a' using the provided predicate.--- Relies on 'fforMaybe'.-ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a-ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing+ffilter :: Filterable f => (a -> Bool) -> f a -> f a+ffilter = W.filter -- | Filter 'Left's from 'f (Either a b)' into 'a'.-filterLeft :: FunctorMaybe f => f (Either a b) -> f a-filterLeft = fmapMaybe (either Just (const Nothing))+filterLeft :: Filterable f => f (Either a b) -> f a+filterLeft = mapMaybe (either Just (const Nothing)) -- | Filter 'Right's from 'f (Either a b)' into 'b'.-filterRight :: FunctorMaybe f => f (Either a b) -> f b-filterRight = fmapMaybe (either (const Nothing) Just)+filterRight :: Filterable f => f (Either a b) -> f b+filterRight = mapMaybe (either (const Nothing) Just) -- | Left-biased event union (prefers left event on simultaneous -- occurrence).@@ -583,14 +602,19 @@ instance Reflex t => Functor (Event t) where {-# INLINE fmap #-}- fmap f = fmapMaybe $ Just . f+ fmap f = mapMaybe $ Just . f {-# INLINE (<$) #-} x <$ e = fmapCheap (const x) e +-- TODO Remove this instance instance Reflex t => FunctorMaybe (Event t) where {-# INLINE fmapMaybe #-}- fmapMaybe f = push $ return . f+ fmapMaybe = mapMaybe +instance Reflex t => Filterable (Event t) where+ {-# INLINE mapMaybe #-}+ mapMaybe f = push $ return . f+ -- | Never: @'zero' = 'never'@. instance Reflex t => Plus (Event t) where zero = never@@ -807,7 +831,7 @@ fanEither e = let justLeft = either Just (const Nothing) justRight = either (const Nothing) Just- in (fmapMaybe justLeft e, fmapMaybe justRight e)+ in (mapMaybe justLeft e, mapMaybe justRight e) -- | Split the event into separate events for 'This' and 'That' values, -- allowing them to fire simultaneously when the input value is 'These'.@@ -819,7 +843,7 @@ that (That y) = Just y that (These _ y) = Just y that _ = Nothing- in (fmapMaybe this e, fmapMaybe that e)+ in (mapMaybe this e, mapMaybe that e) -- | Split the event into an 'EventSelector' that allows efficient selection of -- the individual 'Event's.@@ -998,14 +1022,16 @@ difference :: Reflex t => Event t a -> Event t b -> Event t a difference = alignEventWithMaybe $ \case This a -> Just a- _ -> Nothing+ _ -> Nothing +-- | Zips two values by taking the union of their shapes and combining with the provided function.+-- 'Nothing' values are dropped. alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c-alignEventWithMaybe f ea eb =- fmapMaybe (f <=< dmapToThese)- $ merge- $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]+alignEventWithMaybe f ea eb = mapMaybe (f <=< dmapToThese) $+ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb] +-- | Produces an 'Event' that fires only when the input event fires with a 'DSum' key that+-- matches the provided key. filterEventKey :: forall t m k v a. ( Reflex t@@ -1023,7 +1049,10 @@ Nothing -> Nothing takeWhileJustE f kv' -+-- | "Factor" the input 'DSum' 'Event' to produce an 'Event' which+-- fires when the 'DSum' key changes and contains both the value of the+-- 'DSum' at switchover and an 'Event' of values produced by subsequent+-- firings of the input 'Event' that do not change the 'DSum' key. factorEvent :: forall t m k v a. ( Reflex t@@ -1073,26 +1102,94 @@ mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c) -accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'+-- with the provided function. See 'foldDyn'.+accumDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> a)+ -> a+ -> Event t b+ -> m (Dynamic t a) accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o-accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)++-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'+-- with the provided 'PushM' action.+accumMDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> PushM t a)+ -> a+ -> Event t b+ -> m (Dynamic t a) accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o-accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)++-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'+-- with the provided function, discarding 'Nothing' results.+accumMaybeDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> Maybe a)+ -> a+ -> Event t b+ -> m (Dynamic t a) accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o-accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)++-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'+-- with the provided 'PushM' action, discarding 'Nothing' results.+accumMaybeMDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> PushM t (Maybe a))+ -> a+ -> Event t b+ -> m (Dynamic t a) accumMaybeMDyn f z e = do rec let e' = flip push e $ \o -> do v <- sample $ current d' f v o d' <- holdDyn z e' return d'-mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)++-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'+-- with a function that both accumulates and produces a value to fire+-- as an 'Event'. Returns both the accumulated value and an 'Event'.+mapAccumDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> (a, c))+ -> a+ -> Event t b+ -> m (Dynamic t a, Event t c) mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o-mapAccumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)++-- | Similar to 'mapAccumDyn' except that the combining function is a+-- 'PushM' action.+mapAccumMDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> PushM t (a, c))+ -> a+ -> Event t b+ -> m (Dynamic t a, Event t c) mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o-mapAccumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)++-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event' with+-- a function that both optionally accumulates and optionally produces+-- a value to fire as a separate output 'Event'.+-- Note that because 'Nothing's are discarded in both cases, the output+-- 'Event' may fire even though the output 'Dynamic' has not changed, and+-- the output 'Dynamic' may update even when the output 'Event' is not firing.+mapAccumMaybeDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> (Maybe a, Maybe c))+ -> a+ -> Event t b+ -> m (Dynamic t a, Event t c) mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o-mapAccumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)++-- | Like 'mapAccumMaybeDyn' except that the combining function is a+-- 'PushM' action.+mapAccumMaybeMDyn+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> PushM t (Maybe a, Maybe c))+ -> a+ -> Event t b+ -> m (Dynamic t a, Event t c) mapAccumMaybeMDyn f z e = do rec let e' = flip push e $ \o -> do v <- sample $ current d'@@ -1100,18 +1197,42 @@ return $ case result of (Nothing, Nothing) -> Nothing _ -> Just result- d' <- holdDyn z $ fmapMaybe fst e'- return (d', fmapMaybe snd e')+ d' <- holdDyn z $ mapMaybe fst e'+ return (d', mapMaybe snd e') +-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'+-- with the provided function. {-# INLINE accumB #-}-accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)+accumB+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> a)+ -> a+ -> Event t b+ -> m (Behavior t a) accumB f = accumMaybeB $ \v o -> Just $ f v o++-- | Like 'accumB' except that the combining function is a 'PushM' action. {-# INLINE accumMB #-}-accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)+accumMB+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> PushM t a)+ -> a+ -> Event t b+ -> m (Behavior t a) accumMB f = accumMaybeMB $ \v o -> Just <$> f v o++-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'+-- with the provided function, discarding 'Nothing' results. {-# INLINE accumMaybeB #-}-accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)+accumMaybeB+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> Maybe a)+ -> a+ -> Event t b+ -> m (Behavior t a) accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o++-- | Like 'accumMaybeB' except that the combining function is a 'PushM' action. {-# INLINE accumMaybeMB #-} accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a) accumMaybeMB f z e = do@@ -1120,16 +1241,42 @@ f v o d' <- hold z e' return d'++-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'+-- with a function that both accumulates and produces a value to fire+-- as an 'Event'. Returns both the accumulated value and an 'Event'. {-# INLINE mapAccumB #-}-mapAccumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)+mapAccumB+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> (a, c))+ -> a+ -> Event t b+ -> m (Behavior t a, Event t c) mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o++-- | Like 'mapAccumB' except that the combining function is a 'PushM' action. {-# INLINE mapAccumMB #-}-mapAccumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)+mapAccumMB+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> PushM t (a, c))+ -> a+ -> Event t b+ -> m (Behavior t a, Event t c) mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o++-- | Accumulate a 'Behavior' by folding occurrences of an 'Event' with+-- a function that both optionally accumulates and optionally produces+-- a value to fire as a separate output 'Event'. 'Nothing's are discarded. {-# INLINE mapAccumMaybeB #-}-mapAccumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)+mapAccumMaybeB+ :: (Reflex t, MonadHold t m, MonadFix m)+ => (a -> b -> (Maybe a, Maybe c))+ -> a+ -> Event t b+ -> m (Behavior t a, Event t c) mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o +-- | LIke 'mapAccumMaybeB' except that the combining function is a 'PushM' action. {-# INLINE mapAccumMaybeMB #-} mapAccumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c) mapAccumMaybeMB f z e = do@@ -1139,8 +1286,8 @@ return $ case result of (Nothing, Nothing) -> Nothing _ -> Just result- d' <- hold z $ fmapMaybe fst e'- return (d', fmapMaybe snd e')+ d' <- hold z $ mapMaybe fst e'+ return (d', mapMaybe snd e') -- | Accumulate occurrences of an 'Event', producing an output occurrence each -- time. Discard the underlying 'Accumulator'.@@ -1284,38 +1431,53 @@ -- Cheap Functions ------------------ +-- | A "cheap" version of 'pushAlways'. See the performance note on 'pushCheap'. {-# INLINE pushAlwaysCheap #-} pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b pushAlwaysCheap f = pushCheap (fmap Just . f) +-- | A "cheap" version of 'mapMaybe'. See the performance note on 'pushCheap'.+{-# INLINE mapMaybeCheap #-}+mapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b+mapMaybeCheap f = pushCheap $ return . f++-- | An alias for 'mapMaybeCheap' {-# INLINE fmapMaybeCheap #-} fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b-fmapMaybeCheap f = pushCheap $ return . f+fmapMaybeCheap = mapMaybeCheap ++-- | A "cheap" version of 'fforMaybe'. See the performance note on 'pushCheap'. {-# INLINE fforMaybeCheap #-} fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b-fforMaybeCheap = flip fmapMaybeCheap+fforMaybeCheap = flip mapMaybeCheap +-- | A "cheap" version of 'ffor'. See the performance note on 'pushCheap'. {-# INLINE fforCheap #-} fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b fforCheap = flip fmapCheap +-- | A "cheap" version of 'fmap'. See the performance note on 'pushCheap'. {-# INLINE fmapCheap #-} fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b fmapCheap f = pushCheap $ return . Just . f +-- | A "cheap" version of 'tag'. See the performance note on 'pushCheap'. {-# INLINE tagCheap #-} tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b tagCheap b = pushAlwaysCheap $ \_ -> sample b +-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'. {-# INLINE mergeWithCheap #-} mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a mergeWithCheap = mergeWithCheap' id +-- | A "cheap" version of 'mergeWithCheap''. See the performance note on 'pushCheap'. {-# INLINE mergeWithCheap' #-} mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f +-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'. {-# INLINE mergeWithFoldCheap' #-} mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b mergeWithFoldCheap' f es =@@ -1327,28 +1489,6 @@ -------------------------------------------------------------------------------- -- Deprecated functions ------------------------------------------------------------------------------------ | Create a new 'Event' that occurs if at least one of the supplied 'Event's--- occurs. If both occur at the same time they are combined using 'mappend'.-{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}-appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a-appendEvents = alignWith $ mergeThese mappend---- | Alias for 'headE'-{-# DEPRECATED onceE "Use 'headE' instead" #-}-onceE :: MonadHold t m => Event t a -> m (Event t a)-onceE = headE---- | Run both sides of a 'These' monadically, combining the results.-{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}-#ifdef USE_TEMPLATE_HASKELL-{-# ANN sequenceThese "HLint: ignore Use fmap" #-}-#endif-sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)-sequenceThese t = case t of- This ma -> fmap This ma- These ma mb -> liftM2 These ma mb- That mb -> fmap That mb {-# DEPRECATED switchPromptly "Use 'switchHoldPromptly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-} switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
src/Reflex/Collection.hs view
@@ -10,12 +10,14 @@ #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} #endif+-- |+-- Module:+-- Reflex.Collection module Reflex.Collection ( -- * Widgets on Collections listHoldWithKey , listWithKey- , listWithKey' , listWithKeyShallowDiff , listViewWithKey , selectViewListWithKey@@ -38,6 +40,9 @@ import Reflex.Dynamic import Reflex.PostBuild.Class +-- | Create a set of widgets based on the provided 'Map'. When the+-- input 'Event' fires, remove widgets for keys with the value 'Nothing'+-- and add/replace widgets for keys with 'Just' values. listHoldWithKey :: forall t m k v a . (Ord k, Adjustable t m, MonadHold t m)@@ -96,15 +101,6 @@ listHoldWithKey Map.empty changeVals $ \k v -> mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k) -{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}-listWithKey'- :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m)- => Map k v- -> Event t (Map k (Maybe v))- -> (k -> v -> Event t v -> m a)- -> m (Dynamic t (Map k a))-listWithKey' = listWithKeyShallowDiff- -- | Display the given map of items (in key order) using the builder -- function provided, and update it with the given event. 'Nothing' -- update entries will delete the corresponding children, and 'Just'@@ -182,6 +178,8 @@ return $ fmap ((,) k) selectSelf return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild +-- | Like 'selectViewListWithKey' but discards the value of the list+-- item widget's output 'Event'. selectViewListWithKey_ :: forall t m k v a . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
src/Reflex/Dynamic.hs view
@@ -17,8 +17,12 @@ #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} #endif--- | This module contains various functions for working with 'Dynamic' values.--- 'Dynamic' and its primitives have been moved to the 'Reflex' class.+-- |+-- Module:+-- Reflex.Dynamic+-- Description:+-- This module contains various functions for working with 'Dynamic' values.+-- 'Dynamic' and its primitives have been moved to the 'Reflex' class. module Reflex.Dynamic ( -- * Basics Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update@@ -70,32 +74,12 @@ , distributeFHListOverDynPure -- * Unsafe , unsafeDynamic- -- * Deprecated functions- , apDyn- , attachDyn- , attachDynWith- , attachDynWithMaybe- , collectDyn- , combineDyn- , distributeDMapOverDyn- , distributeFHListOverDyn- , forDyn- , getDemuxed- , joinDyn- , mapDyn- , mconcatDyn- , nubDyn- , splitDyn- , tagDyn- , uniqDyn- , uniqDynBy ) where import Data.Functor.Compose import Data.Functor.Misc import Reflex.Class -import Control.Applicative ((<*>)) import Control.Monad import Control.Monad.Fix import Control.Monad.Identity@@ -103,11 +87,10 @@ import Data.Dependent.Map (DMap) import qualified Data.Dependent.Map as DMap import Data.Dependent.Sum (DSum (..))-import Data.Functor.Product import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..)) import Data.Map (Map) import Data.Maybe-import Data.Monoid hiding (Product)+import Data.Monoid ((<>)) import Data.These import Debug.Trace@@ -236,7 +219,7 @@ -- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic' -- with the current values of the 'Dynamic's in a map. joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)-joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure+joinDynThroughMap = join . fmap distributeMapOverDynPure -- | Print the value of the 'Dynamic' when it is first read and on each -- subsequent change that is observed (as 'traceEvent'), prefixed with the@@ -352,7 +335,7 @@ -- -- > demuxed (demux d) k === fmap (== k) d ----- However, the when getDemuxed is used multiple times, the complexity is only+-- However, when getDemuxed is used multiple times, the complexity is only -- /O(log(n))/, rather than /O(n)/ for fmap. data Demux t k = Demux { demuxValue :: Behavior t k , demuxSelector :: EventSelector t (Const2 k Bool)@@ -506,7 +489,7 @@ -- | Convert a datastructure whose constituent parts are all 'Dynamic's into a -- single 'Dynamic' whose value represents all the current values of the input's--- consitutent 'Dynamic's.+-- constituent 'Dynamic's. collectDynPure :: ( RebuildSortedHList (HListElems b) , IsHList a, IsHList b , AllAreFunctors (Dynamic t) (HListElems b)@@ -547,134 +530,3 @@ #if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139 #endif------------------------------------------------------------------------------------- Deprecated functions------------------------------------------------------------------------------------- | Map a function over a 'Dynamic'.-{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}-mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)-mapDyn f = return . fmap f---- | Flipped version of 'mapDyn'.-{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}-forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)-forDyn a = return . ffor a---- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value--- of the tuple.-{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}-splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)-splitDyn = return . splitDynPure---- | Merge the 'Dynamic' values using their 'Monoid' instance.-{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}-mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)-mconcatDyn = return . mconcat---- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.-{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}-distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))-distributeDMapOverDyn = return . distributeDMapOverDynPure---- | Merge two 'Dynamic's into a new one using the provided function. The new--- 'Dynamic' changes its value each time one of the original 'Dynamic's changes--- its value.-{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}-combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)-combineDyn f a b = return $ zipDynWith f a b---- | A psuedo applicative version of ap for 'Dynamic'. Example useage:------ > do--- > person <- Person `mapDyn` dynFirstName--- > `apDyn` dynListName--- > `apDyn` dynAge--- > `apDyn` dynAddress-{-# 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" #-}-#ifdef USE_TEMPLATE_HASKELL-{-# ANN apDyn "HLint: ignore Use fmap" #-}-#endif-apDyn :: forall t m a b. (Reflex t, Monad m)- => m (Dynamic t (a -> b))- -> Dynamic t a- -> m (Dynamic t b)-apDyn m a = fmap (<*> a) m----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--- | This function no longer needs to be monadic, so it has been replaced by--- 'demuxed', which is pure.-{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}-getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)-getDemuxed d = return . demuxed d---- | This function no longer needs to be monadic, so it has been replaced by--- 'distributeFHListOverDynPure', which is pure.-{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}-distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))-distributeFHListOverDyn = return . distributeFHListOverDynPure---- | This function no longer needs to be monadic, so it has been replaced by--- 'collectDynPure', which is pure.-{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}-collectDyn :: ( RebuildSortedHList (HListElems b)- , IsHList a, IsHList b- , AllAreFunctors (Dynamic t) (HListElems b)- , Reflex t, Monad m- , HListElems a ~ FunctorList (Dynamic t) (HListElems b)- ) => a -> m (Dynamic t b)-collectDyn = return . collectDynPure---- | This function has been renamed to 'tagPromptlyDyn' to clarify its--- semantics.-{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}-tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a-tagDyn = tagPromptlyDyn---- | This function has been renamed to 'attachPromptlyDyn' to clarify its--- semantics.-{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}-attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)-attachDyn = attachPromptlyDyn---- | This function has been renamed to 'attachPromptlyDynWith' to clarify its--- semantics.-{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}-attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c-attachDynWith = attachPromptlyDynWith---- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify--- its semantics.-{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}-attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c-attachDynWithMaybe = attachPromptlyDynWithMaybe---- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s--- current value will always be equal to the current value's current value, and--- will change whenever either the inner or the outer (or both) values change.-{-# DEPRECATED joinDyn "Use 'join' instead" #-}-joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a-joinDyn = join---- | WARNING: This function is only pure if @a@'s 'Eq' instance tests--- representational equality. Use 'holdUniqDyn' instead, which is pure in all--- circumstances. Also, note that, unlike 'nub', this function does not prevent--- all recurrences of a value, only consecutive recurrences.-{-# DEPRECATED nubDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}-nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a-nubDyn = uniqDyn---- | WARNING: This function is only pure if @a@'s 'Eq' instance tests--- representational equality. Use 'holdUniqDyn' instead, which is pure in all--- circumstances.-{-# DEPRECATED uniqDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}-uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a-uniqDyn = uniqDynBy (==)---- | WARNING: This function is impure. Use 'holdUniqDynBy' instead.-{-# DEPRECATED uniqDynBy "Use 'holdUniqDynBy' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}-uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a-uniqDynBy eq d =- let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)- in unsafeDynamic (current d) e'
src/Reflex/Dynamic/TH.hs view
@@ -13,9 +13,6 @@ ( qDynPure , unqDyn , mkDynPure- -- * Deprecated functions- , qDyn- , mkDyn ) where import Reflex.Dynamic@@ -23,7 +20,7 @@ import Control.Monad.State import Data.Data import Data.Generics-import Data.Monoid+import Data.Monoid ((<>)) import qualified Language.Haskell.Exts as Hs import qualified Language.Haskell.Meta.Syntax.Translate as Hs import Language.Haskell.TH@@ -107,24 +104,3 @@ reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName'))) | modName == modName' && occName == occName' = 'unqMarker reinstateUnqDyn x = x------------------------------------------------------------------------------------- Deprecated-----------------------------------------------------------------------------------{-# DEPRECATED qDyn "Instead of $(qDyn x), use return $(qDynPure x)" #-}--- | Like 'qDynPure', but wraps its result monadically using 'return'. This is--- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.-qDyn :: Q Exp -> Q Exp-qDyn qe = [| return $(qDynPure qe) |]--{-# DEPRECATED mkDyn "Instead of [mkDyn| x |], use return [mkDynPure| x |]" #-}--- | Like 'mkDynPure', but wraps its result monadically using 'return'. This is--- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.-mkDyn :: QuasiQuoter-mkDyn = QuasiQuoter- { quoteExp = \s -> [| return $(mkDynExp s) |]- , quotePat = error "mkDyn: pattern splices are not supported"- , quoteType = error "mkDyn: type splices are not supported"- , quoteDec = error "mkDyn: declaration splices are not supported"- }
src/Reflex/DynamicWriter/Base.hs view
@@ -22,6 +22,7 @@ import Control.Monad.Exception import Control.Monad.Identity import Control.Monad.IO.Class+import Control.Monad.Primitive import Control.Monad.Reader import Control.Monad.Ref import Control.Monad.State.Strict@@ -34,7 +35,7 @@ import qualified Data.IntMap as IntMap import Data.Map (Map) import qualified Data.Map as Map-import Data.Semigroup+import Data.Semigroup (Semigroup(..)) import Data.Some (Some) import Data.These @@ -87,7 +88,7 @@ noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered? --- | A basic implementation of 'MonadDynamicWriter'.+-- | A basic implementation of 'DynamicWriter'. newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order @@ -115,7 +116,7 @@ (result, ws) <- runStateT a [] return (result, mconcat $ reverse ws) -instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where+instance (Monad m, Monoid w, Reflex t) => DynamicWriter t w (DynamicWriterT t w m) where tellDyn w = DynamicWriterT $ modify (w :) instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where@@ -139,6 +140,10 @@ instance MonadState s m => MonadState s (DynamicWriterT t w m) where get = lift get put = lift . put++instance PrimMonad m => PrimMonad (DynamicWriterT t w m) where+ type PrimState (DynamicWriterT t w m) = PrimState m+ primitive = lift . primitive newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
src/Reflex/DynamicWriter/Class.hs view
@@ -1,4 +1,5 @@--- | This module defines the 'MonadDynamicWriter' class.+-- | This module defines the 'DynamicWriter' class.+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}@@ -8,17 +9,21 @@ {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} #endif module Reflex.DynamicWriter.Class- ( MonadDynamicWriter (..)+ ( MonadDynamicWriter+ , DynamicWriter(..) ) where import Control.Monad.Reader (ReaderT, lift) import Reflex.Class (Dynamic) +{-# DEPRECATED MonadDynamicWriter "Use 'DynamicWriter' instead" #-}+type MonadDynamicWriter = DynamicWriter+ -- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn' -- and combines them monoidally to provide a 'Dynamic' result.-class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where+class (Monad m, Monoid w) => DynamicWriter t w m | m -> t w where tellDyn :: Dynamic t w -> m () -instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where+instance DynamicWriter t w m => DynamicWriter t w (ReaderT r m) where tellDyn = lift . tellDyn
src/Reflex/EventWriter/Base.hs view
@@ -25,7 +25,7 @@ import Reflex.Adjustable.Class import Reflex.Class import Reflex.EventWriter.Class (EventWriter, tellEvent)-import Reflex.DynamicWriter.Class (MonadDynamicWriter, tellDyn)+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn) import Reflex.Host.Class import Reflex.PerformEvent.Class import Reflex.PostBuild.Class@@ -278,7 +278,7 @@ askQueryResult = lift askQueryResult queryIncremental = lift . queryIncremental -instance MonadDynamicWriter t w m => MonadDynamicWriter t w (EventWriterT t v m) where+instance DynamicWriter t w m => DynamicWriter t w (EventWriterT t v m) where tellDyn = lift . tellDyn instance PrimMonad m => PrimMonad (EventWriterT t w m) where
src/Reflex/FastWeak.hs view
@@ -6,14 +6,18 @@ {-# LANGUAGE JavaScriptFFI #-} #endif --- | 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value--- referred to by a 'FastWeak' stays live while the ticket is held (live).+-- |+-- Module:+-- Reflex.FastWeak+-- Description:+-- 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value+-- referred to by a 'FastWeak' stays live while the ticket is held (live). ----- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper--- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same. ----- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly--- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS. module Reflex.FastWeak ( FastWeakTicket , FastWeak
src/Reflex/FunctorMaybe.hs view
@@ -1,43 +1,45 @@--- | This module defines the FunctorMaybe class, which extends Functors with the--- ability to delete values.+{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE StandaloneDeriving #-}++-- |+-- Module:+-- Reflex.FunctorMaybe+-- Description:+-- This module defines the FunctorMaybe class, which extends Functors with the+-- ability to delete values. module Reflex.FunctorMaybe ( FunctorMaybe (..) ) where import Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap import Data.Map (Map)-import qualified Data.Map as Map-import Data.Maybe+#if MIN_VERSION_base(4,9,0)+import Data.Semigroup (Option(..))+#endif+import Data.Witherable --TODO: See if there's a better class in the standard libraries already -- | A class for values that combines filtering and mapping using 'Maybe'.--- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the--- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,--- and e.g. 'Event' is instance of neither.------ A definition of 'fmapMaybe' must satisfy the following laws:------ [/identity/]--- @'fmapMaybe' 'Just' ≡ 'id'@------ [/composition/]--- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@.+{-# DEPRECATED FunctorMaybe "Use 'Filterable' from Data.Witherable instead" #-} class FunctorMaybe f where -- | Combined mapping and filtering function. fmapMaybe :: (a -> Maybe b) -> f a -> f b --- | @fmapMaybe = (=<<) instance FunctorMaybe Maybe where- fmapMaybe = (=<<)+ fmapMaybe = mapMaybe --- | @fmapMaybe = mapMaybe@+#if MIN_VERSION_base(4,9,0)+deriving instance FunctorMaybe Option+#endif+ instance FunctorMaybe [] where fmapMaybe = mapMaybe instance FunctorMaybe (Map k) where- fmapMaybe = Map.mapMaybe+ fmapMaybe = mapMaybe instance FunctorMaybe IntMap where- fmapMaybe = IntMap.mapMaybe+ fmapMaybe = mapMaybe
src/Reflex/Network.hs view
@@ -3,6 +3,11 @@ #ifdef USE_REFLEX_OPTIMIZER {-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-} #endif+-- |+-- Module:+-- Reflex.Network+-- Description:+-- This module provides combinators for building FRP graphs/networks and modifying them dynamically. module Reflex.Network ( networkView , networkHold@@ -14,16 +19,17 @@ import Reflex.NotReady.Class import Reflex.PostBuild.Class --- | Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.--- The returned Event of network results occurs when the Dynamic does.--- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events that would typically be flattened (via 'switchPromptly').+-- | A 'Dynamic' "network": Takes a 'Dynamic' of network-creating actions and replaces the network whenever the 'Dynamic' updates.+-- The returned Event of network results fires when the 'Dynamic' updates.+-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events, where the outer 'Event' fires+-- when switching networks. Such an 'Event' would typically be flattened (via 'switchPromptly'). networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a) networkView child = do postBuild <- getPostBuild let newChild = leftmost [updated child, tagCheap (current child) postBuild] snd <$> runWithReplace notReady newChild --- | Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the Event fires.+-- | Given an initial "network" and an 'Event' of network-creating actions, create a network that is recreated whenever the Event fires. -- The returned Dynamic of network results occurs when the Event does. -- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened. networkHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
src/Reflex/NotReady/Class.hs view
@@ -15,7 +15,9 @@ import Control.Monad.Reader (ReaderT) import Control.Monad.Trans+import Control.Monad.Trans.Writer (WriterT) +import Reflex.BehaviorWriter.Base (BehaviorWriterT) import Reflex.Class import Reflex.DynamicWriter.Base (DynamicWriterT) import Reflex.EventWriter.Base (EventWriterT)@@ -39,6 +41,10 @@ notReadyUntil = lift . notReadyUntil notReady = lift notReady +instance (NotReady t m, Monoid w) => NotReady t (WriterT w m) where+ notReadyUntil = lift . notReadyUntil+ notReady = lift notReady+ instance NotReady t m => NotReady t (PostBuildT t m) where notReadyUntil = lift . notReadyUntil notReady = lift notReady@@ -48,6 +54,10 @@ notReady = lift notReady instance NotReady t m => NotReady t (DynamicWriterT t w m) where+ notReadyUntil = lift . notReadyUntil+ notReady = lift notReady++instance NotReady t m => NotReady t (BehaviorWriterT t w m) where notReadyUntil = lift . notReadyUntil notReady = lift notReady
src/Reflex/Optimizer.hs view
@@ -1,10 +1,14 @@--- | This module provides a GHC plugin designed to improve code that uses--- Reflex. Currently, it just adds an INLINABLE pragma to any top-level--- definition that doesn't have an explicit inlining pragma. In the future,--- additional optimizations are likely to be added. {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}+-- |+-- Module:+-- Reflex.Optimizer+-- Description:+-- This module provides a GHC plugin designed to improve code that uses+-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level+-- definition that doesn't have an explicit inlining pragma. In the future,+-- additional optimizations are likely to be added. module Reflex.Optimizer ( plugin ) where
src/Reflex/Patch.hs view
@@ -1,6 +1,10 @@--- | This module defines the 'Patch' class, which is used by Reflex to manage--- changes to 'Reflex.Class.Incremental' values. {-# LANGUAGE TypeFamilies #-}+-- |+-- Module:+-- Reflex.Patch+-- Description:+-- This module defines the 'Patch' class, which is used by Reflex to manage+-- changes to 'Reflex.Class.Incremental' values. module Reflex.Patch ( module Reflex.Patch , module X@@ -17,11 +21,9 @@ import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements, patchMapWithMoveNewElementsMap, unPatchMapWithMove, unsafePatchMapWithMove)-+import Data.Map.Monoidal (MonoidalMap) import Data.Semigroup (Semigroup (..), (<>)) ----- Patches based on commutative groups- -- | A 'Group' is a 'Monoid' where every element has an inverse. class (Semigroup q, Monoid q) => Group q where negateG :: q -> q@@ -37,3 +39,8 @@ instance Additive p => Patch (AdditivePatch p) where type PatchTarget (AdditivePatch p) = p apply (AdditivePatch p) q = Just $ p <> q++instance (Ord k, Group q) => Group (MonoidalMap k q) where+ negateG = fmap negateG++instance (Ord k, Additive q) => Additive (MonoidalMap k q)
src/Reflex/Patch/Class.hs view
@@ -4,7 +4,7 @@ import Control.Monad.Identity import Data.Maybe-import Data.Semigroup+import Data.Semigroup (Semigroup(..)) -- | A 'Patch' type represents a kind of change made to a datastructure. --
src/Reflex/Profiled.hs view
@@ -8,6 +8,12 @@ {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-}+-- |+-- Module:+-- Reflex.Profiled+-- Description:+-- This module contains an instance of the 'Reflex' class that provides+-- profiling/cost-center information. module Reflex.Profiled where import Control.Lens hiding (children)
src/Reflex/Pure.hs view
@@ -13,11 +13,13 @@ -- * MonadSample (Pure t) ((->) t) -- * MonadHold (Pure t) ((->) t) {-# OPTIONS_GHC -fno-warn-orphans #-}---- | This module provides a pure implementation of Reflex, which is intended to--- serve as a reference for the semantics of the Reflex class. All--- implementations of Reflex should produce the same results as this--- implementation, although performance and laziness/strictness may differ.+-- |+-- Module: Reflex.Pure+-- Description:+-- This module provides a pure implementation of Reflex, which is intended to+-- serve as a reference for the semantics of the Reflex class. All+-- implementations of Reflex should produce the same results as this+-- implementation, although performance and laziness/strictness may differ. module Reflex.Pure ( Pure , Behavior (..)
src/Reflex/Query/Base.hs view
@@ -74,26 +74,19 @@ getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q] getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w -{--let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q- sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty- bs' = fmapCheap snd $ r'- patches = unsafeBuildIncremental (sampleBs bs0) $- flip pushCheap bs' $ \bs -> do- p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)- return (Just (AdditivePatch p))--}+maskMempty :: (Eq a, Monoid a) => a -> Maybe a+maskMempty x = if x == mempty then Nothing else Just x -instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where+instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where runWithReplace (QueryT a0) a' = do ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a' let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty bs' = fmapCheap snd $ r' bbs <- hold bs0 bs'- let patches = flip pushAlwaysCheap bs' $ \newBs -> do+ let patches = flip pushCheap bs' $ \newBs -> do oldBs <- sample bbs- (~~) <$> sampleBs newBs <*> sampleBs oldBs+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs) QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs QueryT $ lift $ tellEvent patches return (r0, fmapCheap fst r')@@ -171,19 +164,19 @@ let p k bs = case Map.lookup k bs0 of Nothing -> case bs of -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.- Nothing -> return mempty+ Nothing -> return Nothing -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.- Just newBs -> sampleBs newBs+ Just newBs -> maskMempty <$> sampleBs newBs Just oldBs -> case bs of -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state- Nothing -> negateG <$> sampleBs oldBs+ Nothing -> maskMempty . negateG <$> sampleBs oldBs -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state -- composed with the sampling the child's new state.- Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs) -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the -- child patches and wrap them in AdditivePatch.- patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'- return (apply pbs bs0, Just patch)+ patch <- fold <$> Map.traverseWithKey p bs'+ return (apply pbs bs0, AdditivePatch <$> patch) (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs' tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch return (liftedResult0, liftedResult')@@ -216,28 +209,28 @@ p k bs = case Map.lookup k bs0 of Nothing -> case MapWithMove._nodeInfo_from bs of -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.- MapWithMove.From_Delete -> return mempty+ MapWithMove.From_Delete -> return Nothing -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.- MapWithMove.From_Insert newBs -> sampleBs newBs+ MapWithMove.From_Insert newBs -> maskMempty <$> sampleBs newBs MapWithMove.From_Move k' -> case Map.lookup k' bs0 of- Nothing -> return mempty- Just newBs -> sampleBs newBs+ Nothing -> return Nothing+ Just newBs -> maskMempty <$> sampleBs newBs Just oldBs -> case MapWithMove._nodeInfo_from bs of -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state- MapWithMove.From_Delete -> negateG <$> sampleBs oldBs+ MapWithMove.From_Delete -> maskMempty . negateG <$> sampleBs oldBs -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state -- composed with the sampling the child's new state.- MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs+ MapWithMove.From_Insert newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs) MapWithMove.From_Move k'- | k' == k -> return mempty+ | k' == k -> return Nothing | otherwise -> case Map.lookup k' bs0 of -- If we are moving from a non-existent key, that is a delete- Nothing -> negateG <$> sampleBs oldBs- Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs+ Nothing -> maskMempty . negateG <$> sampleBs oldBs+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs) -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the -- child patches and wrap them in AdditivePatch.- patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'- return (apply pbs bs0, Just patch)+ patch <- fold <$> Map.traverseWithKey p bs'+ return (apply pbs bs0, AdditivePatch <$> patch) (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs' tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch return (liftedResult0, liftedResult')@@ -340,5 +333,5 @@ instance EventWriter t w m => EventWriter t w (QueryT t q m) where tellEvent = lift . tellEvent -instance MonadDynamicWriter t w m => MonadDynamicWriter t w (QueryT t q m) where+instance DynamicWriter t w m => DynamicWriter t w (QueryT t q m) where tellDyn = lift . tellDyn
src/Reflex/Query/Class.hs view
@@ -27,7 +27,7 @@ import Data.Ix import Data.Map.Monoidal (MonoidalMap) import qualified Data.Map.Monoidal as MonoidalMap-import Data.Semigroup+import Data.Semigroup (Semigroup(..)) import Foreign.Storable import Reflex.Class
src/Reflex/Requester/Base.hs view
@@ -21,6 +21,7 @@ module Reflex.Requester.Base ( RequesterT (..) , runRequesterT+ , withRequesterT , runWithReplaceRequesterTWith , traverseIntMapWithKeyWithAdjustRequesterTWith , traverseDMapWithKeyWithAdjustRequesterTWith@@ -30,6 +31,10 @@ , forRequesterData , requesterDataToList , singletonRequesterData+ , matchResponsesWithRequests+ , multiEntry+ , unMultiEntry+ , requesting' ) where import Reflex.Class@@ -44,6 +49,7 @@ import Control.Applicative (liftA2) import Control.Monad.Exception import Control.Monad.Identity+import Control.Monad.Primitive import Control.Monad.Reader import Control.Monad.Ref import Control.Monad.State.Strict@@ -244,6 +250,10 @@ deriving instance PostBuild t m => PostBuild t (RequesterT t request response m) deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m) +instance PrimMonad m => PrimMonad (RequesterT t request response m) where+ type PrimState (RequesterT t request response m) = PrimState m+ primitive = lift . primitive+ -- TODO: Monoid and Semigroup can likely be derived once StateT has them. instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where mempty = pure mempty@@ -257,7 +267,6 @@ -- requests are made, and responses should be provided in the input 'Event'. -- The 'Tag' keys will be used to return the responses to the same place the -- requests were issued.- runRequesterT :: (Reflex t, Monad m) => RequesterT t request response m a -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse@@ -267,6 +276,20 @@ coerceEvent responses return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s) +-- | Map a function over the request and response of a 'RequesterT'+withRequesterT+ :: (Reflex t, MonadFix m)+ => (forall x. req x -> req' x) -- ^ The function to map over the request+ -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response+ -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed+ -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT'+withRequesterT freq frsp child = do+ rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp'+ (a, req) <- lift $ runRequesterT child rsp+ rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $+ fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req+ return a+ instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where type Request (RequesterT t request response m) = request type Response (RequesterT t request response m) = response@@ -370,7 +393,7 @@ pack = Entry f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v') f' k (n, v) = do- (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing mapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time? return (fmapCheap (IntMap.singleton n) myRequests, result) ndm' <- numberOccurrencesFrom 1 dm' (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable@@ -418,7 +441,7 @@ pack = Entry f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a) f' k (Compose (n, v)) = do- (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k)) return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result) ndm' <- numberOccurrencesFrom 1 dm' (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'@@ -432,3 +455,84 @@ promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence' requests <- holdIncremental requests0 requests' return (result0, result')++data Decoder rawResponse response =+ forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)++-- | Matches incoming responses with previously-sent requests+-- and uses the provided request "decoder" function to process+-- incoming responses.+matchResponsesWithRequests+ :: forall t rawRequest rawResponse request response m.+ ( MonadFix m+ , MonadHold t m+ , Reflex t+ )+ => (forall a. request a -> (rawRequest, rawResponse -> response a))+ -- ^ Given a request (from 'Requester'), produces the wire format of the+ -- request and a function used to process the associated response+ -> Event t (RequesterData request)+ -- ^ The outgoing requests+ -> Event t (Int, rawResponse)+ -- ^ The incoming responses, tagged by an identifying key+ -> m ( Event t (Map Int rawRequest)+ , Event t (RequesterData response)+ )+ -- ^ A map of outgoing wire-format requests and an event of responses keyed+ -- by the 'RequesterData' key of the associated outgoing request+matchResponsesWithRequests f send recv = do+ rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing+ waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-+ holdIncremental mempty $ leftmost+ [ fmap (\(_, outstanding, _) -> outstanding) outgoing+ , snd <$> incoming+ ]+ let outgoing = processOutgoing nextId send+ incoming = processIncoming waitingFor recv+ return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming)+ where+ -- Tags each outgoing request with an identifying integer key+ -- and returns the next available key, a map of response decoders+ -- for requests for which there are outstanding responses, and the+ -- raw requests to be sent out.+ processOutgoing+ :: Behavior t Int+ -- The next available key+ -> Event t (RequesterData request)+ -- The outgoing request+ -> Event t ( Int+ , PatchMap Int (Decoder rawResponse response)+ , Map Int rawRequest )+ -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests+ processOutgoing nextId out = flip pushAlways out $ \dm -> do+ oldNextId <- sample nextId+ let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do+ n <- get+ put $ succ n+ let (rawReq, rspF) = f v+ return (n, rawReq, Decoder k rspF)+ patchWaitingFor = PatchMap $ Map.fromList $+ (\(n, _, dec) -> (n, Just dec)) <$> result+ toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result+ return (newNextId, patchWaitingFor, toSend)+ -- Looks up the each incoming raw response in a map of response+ -- decoders and returns the decoded response and a patch that can+ -- be used to clear the ID of the consumed response out of the queue+ -- of expected responses.+ processIncoming+ :: Incremental t (PatchMap Int (Decoder rawResponse response))+ -- A map of outstanding expected responses+ -> Event t (Int, rawResponse)+ -- A incoming response paired with its identifying key+ -> Event t (RequesterData response, PatchMap Int v)+ -- The decoded response and a patch that clears the outstanding responses queue+ processIncoming waitingFor inc = flip push inc $ \(n, rawRsp) -> do+ wf <- sample $ currentIncremental waitingFor+ case Map.lookup n wf of+ Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..+ Just (Decoder k rspF) -> do+ let rsp = rspF rawRsp+ return $ Just+ ( singletonRequesterData k rsp+ , PatchMap $ Map.singleton n Nothing+ )
src/Reflex/Spider.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE CPP #-}--- | This module exports all of the user-facing functionality of the 'Spider'--- 'Reflex' engine+-- |+-- Module:+-- Reflex.Spider+-- Description:+-- This module exports all of the user-facing functionality of the 'Spider' 'Reflex' engine module Reflex.Spider ( Spider , SpiderTimeline
src/Reflex/Spider/Internal.hs view
@@ -49,16 +49,16 @@ import Data.IntMap.Strict (IntMap) import qualified Data.IntMap.Strict as IntMap import Data.IORef-import Data.Maybe+import Data.Maybe hiding (mapMaybe) import Data.Monoid ((<>)) import Data.Proxy import Data.These import Data.Traversable+import Data.Witherable (Filterable, mapMaybe) import GHC.Exts import GHC.IORef (IORef (..)) import GHC.Stack import Reflex.FastWeak-import Reflex.FunctorMaybe import System.IO.Unsafe import System.Mem.Weak import Unsafe.Coerce@@ -1128,12 +1128,12 @@ newInvalidatorPull :: Pull x a -> IO (Invalidator x) newInvalidatorPull p = return $! InvalidatorPull p -instance HasSpiderTimeline x => FunctorMaybe (Event x) where- fmapMaybe f = push $ return . f+instance HasSpiderTimeline x => Filterable (Event x) where+ mapMaybe f = push $ return . f instance HasSpiderTimeline x => Align (Event x) where nil = eventNever- align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]+ align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb] data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p) | BuildDyn !(EventM x (PatchTarget p), Event x p)
src/Reflex/Time.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecursiveDo #-}@@ -9,6 +11,11 @@ #ifdef USE_TEMPLATE_HASKELL {-# LANGUAGE TemplateHaskell #-} #endif+-- |+-- Module:+-- Reflex.Time+-- Description:+-- Clocks, timers, and other time-related functions. module Reflex.Time where import Reflex.Class@@ -24,46 +31,50 @@ import Control.Monad.Fix import Control.Monad.IO.Class import Data.Align+import Data.Data (Data) import Data.Fixed+import Data.Semigroup (Semigroup(..)) import Data.Sequence (Seq, (|>)) import qualified Data.Sequence as Seq import Data.These import Data.Time.Clock import Data.Typeable+import GHC.Generics (Generic) import System.Random +-- | Metadata associated with a timer "tick" data TickInfo = TickInfo { _tickInfo_lastUTC :: UTCTime -- ^ UTC time immediately after the last tick. , _tickInfo_n :: Integer- -- ^ Number of time periods since t0+ -- ^ Number of time periods or ticks since the start of the timer , _tickInfo_alreadyElapsed :: NominalDiffTime- -- ^ Amount of time already elapsed in the current tick period.+ -- ^ Amount of time that has elapsed in the current tick period. } deriving (Eq, Ord, Show, Typeable) --- | Special case of 'tickLossyFrom' that uses the post-build event to start the--- tick thread.+-- | Fires an 'Event' once every time provided interval elapses, approximately.+-- The provided 'UTCTime' is used bootstrap the determination of how much time has elapsed with each tick.+-- This is a special case of 'tickLossyFrom' that uses the post-build event to start the tick thread. tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo) tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild --- | Special case of 'tickLossyFrom' that uses the post-build event to start the--- tick thread and the time of the post-build as the tick basis time.+-- | Fires an 'Event' once every time provided interval elapses, approximately.+-- This is a special case of 'tickLossyFrom' that uses the post-build event to start the tick thread and the time of the post-build as the tick basis time. tickLossyFromPostBuildTime :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> m (Event t TickInfo) tickLossyFromPostBuildTime dt = do postBuild <- getPostBuild postBuildTime <- performEvent $ liftIO getCurrentTime <$ postBuild tickLossyFrom' $ (dt,) <$> postBuildTime --- | Send events over time with the given basis time and interval--- If the system starts running behind, occurrences will be dropped rather than buffered--- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the basis time+-- | Fires an 'Event' approximately each time the provided interval elapses. If the system starts running behind, occurrences will be dropped rather than buffered.+-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the provided initial time. tickLossyFrom :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -- ^ The length of a tick interval -> UTCTime- -- ^ The basis time from which intervals count+ -- ^ The basis time from which intervals count and with which the initial calculation of elapsed time will be made. -> Event t a -- ^ Event that starts a tick generation thread. Usually you want this to -- be something like the result of getPostBuild that only fires once. But@@ -71,12 +82,12 @@ -> m (Event t TickInfo) tickLossyFrom dt t0 e = tickLossyFrom' $ (dt, t0) <$ e --- | Generalization of tickLossyFrom that takes dt and t0 in the event.+-- | Generalization of tickLossyFrom that takes the delay and initial time as an 'Event'. tickLossyFrom' :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => Event t (NominalDiffTime, UTCTime) -- ^ Event that starts a tick generation thread. Usually you want this to- -- be something like the result of getPostBuild that only fires once. But+ -- be something like the result of 'getPostBuild' that only fires once. But -- there could be uses for starting multiple timer threads. -> m (Event t TickInfo) tickLossyFrom' e = do@@ -87,12 +98,16 @@ Concurrent.delay $ ceiling $ (fst pair - _tickInfo_alreadyElapsed tick) * 1000000 cb (tick, pair) +-- | Like 'tickLossy', but immediately calculates the first tick and provides a 'Dynamic' that is updated as ticks fire. clockLossy :: (MonadIO m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), PostBuild t m, MonadHold t m, MonadFix m) => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo) clockLossy dt t0 = do initial <- liftIO $ getCurrentTick dt t0 e <- tickLossy dt t0 holdDyn initial e +-- | Generates a 'TickInfo', given the specified interval and timestamp. The 'TickInfo' will include the+-- current time, the number of ticks that have elapsed since the timestamp, and the amount of time that+-- has elapsed since the start time of this tick. getCurrentTick :: NominalDiffTime -> UTCTime -> IO TickInfo getCurrentTick dt t0 = do t <- getCurrentTime@@ -266,6 +281,92 @@ delayed <- delay t outE return outE +data ThrottleState b+ = ThrottleState_Immediate+ | ThrottleState_Buffered (ThrottleBuffer b)+ deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic, Data, Typeable)++data ThrottleBuffer b+ = ThrottleBuffer_Empty -- Empty conflicts with lens, and hiding it would require turning+ -- on PatternSynonyms+ | ThrottleBuffer_Full b+ deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic, Data, Typeable)++instance Semigroup b => Semigroup (ThrottleBuffer b) where+ x <> y = case x of+ ThrottleBuffer_Empty -> y+ ThrottleBuffer_Full b1 -> case y of+ ThrottleBuffer_Empty -> x+ ThrottleBuffer_Full b2 -> ThrottleBuffer_Full $ b1 <> b2+ {-# INLINE (<>) #-}++instance Semigroup b => Monoid (ThrottleBuffer b) where+ mempty = ThrottleBuffer_Empty+ {-# INLINE mempty #-}+ mappend = (<>)+ {-# INLINE mappend #-}++-- | Throttle an input event, ensuring that the output event doesn't occur more often than you are ready for it. If the input event occurs too+-- frequently, the output event will contain semigroup-based summaries of the input firings that happened since the last output firing.+-- If the output event has not occurred recently, occurrences of the input event will cause the output event to fire immediately.+-- The first parameter is a function that receives access to the output event, and should construct an event that fires when the receiver is+-- ready for more input. For example, using @delay 20@ would give a simple time-based throttle.+--+-- NB: The provided lag function must *actually* delay the event.+throttleBatchWithLag :: (MonadFix m, MonadHold t m, PerformEvent t m, Semigroup a) => (Event t () -> m (Event t ())) -> Event t a -> m (Event t a)+-- Invariants:+-- * Immediate mode must turn off whenever output is produced.+-- * Output must be produced whenever immediate mode turns from on to off.+-- * Immediate mode can only go from off to on when the delayed event fires.+-- * Every input firing must go into either an immediate output firing or the+-- buffer, but not both.+-- * An existing full buffer must either stay in the buffer or go to output,+-- but not both.+throttleBatchWithLag lag e = do+ let f state x = case x of -- (Just $ newState, out)+ This a -> -- If only the input event fires+ case state of+ ThrottleState_Immediate -> -- and we're in immediate mode+ -- Immediate mode turns off, and the buffer is empty.+ -- We fire the output event with the input event value immediately.+ (Just $ ThrottleState_Buffered $ ThrottleBuffer_Empty, Just a)+ ThrottleState_Buffered b -> -- and we're not in immediate mode+ -- Immediate mode remains off, and we accumulate the input value.+ -- We don't fire the output event.+ (Just $ ThrottleState_Buffered $ b <> ThrottleBuffer_Full a, Nothing)+ That _ -> -- If only the delayed output event fires,+ case state of+ ThrottleState_Immediate -> -- and we're in immediate mode+ -- Nothing happens.+ (Nothing, Nothing)+ ThrottleState_Buffered ThrottleBuffer_Empty -> -- and the buffer is empty:+ -- Immediate mode turns back on, and the buffer remains empty.+ -- We don't fire.+ (Just ThrottleState_Immediate, Nothing)+ ThrottleState_Buffered (ThrottleBuffer_Full b) -> -- and the buffer is full:+ -- Immediate mode remains off, and the buffer is cleared.+ -- We fire with the buffered value.+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just b)+ These a _ -> -- If both the input and delayed output event fire simultaneously:+ case state of+ ThrottleState_Immediate -> -- and we're in immediate mode+ -- Immediate mode turns off, and the buffer is empty.+ -- We fire with the input event's value, as it is the most recent we have seen at this moment.+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just a)+ ThrottleState_Buffered ThrottleBuffer_Empty -> -- and the buffer is empty:+ -- Immediate mode stays off, and the buffer remains empty.+ -- We fire with the input event's value.+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just a)+ ThrottleState_Buffered (ThrottleBuffer_Full b) -> -- and the buffer is full:+ -- Immediate mode remains off, and the buffer is cleared.+ -- We fire with everything including the buffered value.+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just (b <> a))+ rec (_stateDyn, outE) <- mapAccumMaybeDyn f+ ThrottleState_Immediate -- We start in immediate mode with an empty buffer.+ (align e delayed)+ delayed <- lag (void outE)+ return outE+ #ifdef USE_TEMPLATE_HASKELL makeLensesWith (lensRules & simpleLenses .~ True) ''TickInfo #else@@ -281,4 +382,3 @@ tickInfo_alreadyElapsed f (TickInfo x1 x2 x3) = (\y -> TickInfo x1 x2 y) <$> f x3 {-# INLINE tickInfo_alreadyElapsed #-} #endif-
src/Reflex/Workflow.hs view
@@ -1,7 +1,13 @@ {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-}+-- |+-- Module:+-- Reflex.Workflow+-- Description:+-- Provides a convenient way to describe a series of interrelated widgets that+-- can send data to, invoke, and replace one another. Useful for modeling user interface+-- "workflows." module Reflex.Workflow (- -- * Workflows Workflow (..) , workflow , workflowView@@ -18,21 +24,27 @@ import Reflex.NotReady.Class import Reflex.PostBuild.Class +-- | A widget in a workflow+-- When the 'Event' returned by a 'Workflow' fires, the current 'Workflow' is replaced by the one inside the firing 'Event'. A series of 'Workflow's must share the same return type. newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) } +-- | Runs a 'Workflow' and returns the 'Dynamic' result of the 'Workflow' (i.e., a 'Dynamic' of the value produced by the current 'Workflow' node, and whose update 'Event' fires whenever one 'Workflow' is replaced by another). workflow :: forall t m a. (Reflex t, Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a) workflow w0 = do rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult return $ fmap fst eResult +-- | Similar to 'workflow', but outputs an 'Event' that fires whenever the current 'Workflow' is replaced by the next 'Workflow'. workflowView :: forall t m a. (Reflex t, NotReady t m, Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a) workflowView w0 = do rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace eReplace <- fmap switch $ hold never $ fmap snd eResult return $ fmap fst eResult +-- | Map a function over a 'Workflow', possibly changing the resturn type. mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x) +-- | Map a "cheap" function over a 'Workflow'. Refer to the documentation for 'pushCheap' for more information and performance considerations. mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
test/RequesterT.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-}@@ -20,18 +21,19 @@ main :: IO () main = do- os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $+ os1 <- runApp' (unwrapApp testOrdering) $ [ Just () ] print os1- os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]- <- runApp' (unwrapApp testSimultaneous) $ map Just $- [ This ()- , That ()- , This ()- , These () ()- ]+ let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1+ os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $+ [ This ()+ , That ()+ , This ()+ , These () ()+ ] print os2+ let ![[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]] = os2 return () unwrapRequest :: DSum tag RequestInt -> Int@@ -43,7 +45,7 @@ -> m (Event t [Int]) unwrapApp x appIn = do ((), e) <- runRequesterT (x appIn) never- return $ fmap (map unwrapRequest . DMap.toList) e+ return $ fmap (map unwrapRequest . requesterDataToList) e testOrdering :: ( Response m ~ Identity , Request m ~ RequestInt