packages feed

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 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