diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -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.
diff --git a/Quickref.md b/Quickref.md
--- a/Quickref.md
+++ b/Quickref.md
@@ -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.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/reflex.cabal b/reflex.cabal
--- a/reflex.cabal
+++ b/reflex.cabal
@@ -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
diff --git a/src/Data/AppendMap.hs b/src/Data/AppendMap.hs
--- a/src/Data/AppendMap.hs
+++ b/src/Data/AppendMap.hs
@@ -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)
 
diff --git a/src/Data/Functor/Misc.hs b/src/Data/Functor/Misc.hs
--- a/src/Data/Functor/Misc.hs
+++ b/src/Data/Functor/Misc.hs
@@ -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
diff --git a/src/Reflex.hs b/src/Reflex.hs
--- a/src/Reflex.hs
+++ b/src/Reflex.hs
@@ -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
diff --git a/src/Reflex/Class.hs b/src/Reflex/Class.hs
--- a/src/Reflex/Class.hs
+++ b/src/Reflex/Class.hs
@@ -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)
diff --git a/src/Reflex/Collection.hs b/src/Reflex/Collection.hs
--- a/src/Reflex/Collection.hs
+++ b/src/Reflex/Collection.hs
@@ -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)
diff --git a/src/Reflex/Dynamic.hs b/src/Reflex/Dynamic.hs
--- a/src/Reflex/Dynamic.hs
+++ b/src/Reflex/Dynamic.hs
@@ -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'
diff --git a/src/Reflex/Dynamic/TH.hs b/src/Reflex/Dynamic/TH.hs
--- a/src/Reflex/Dynamic/TH.hs
+++ b/src/Reflex/Dynamic/TH.hs
@@ -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"
-  }
diff --git a/src/Reflex/DynamicWriter/Base.hs b/src/Reflex/DynamicWriter/Base.hs
--- a/src/Reflex/DynamicWriter/Base.hs
+++ b/src/Reflex/DynamicWriter/Base.hs
@@ -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)
 
diff --git a/src/Reflex/DynamicWriter/Class.hs b/src/Reflex/DynamicWriter/Class.hs
--- a/src/Reflex/DynamicWriter/Class.hs
+++ b/src/Reflex/DynamicWriter/Class.hs
@@ -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
 
diff --git a/src/Reflex/EventWriter/Base.hs b/src/Reflex/EventWriter/Base.hs
--- a/src/Reflex/EventWriter/Base.hs
+++ b/src/Reflex/EventWriter/Base.hs
@@ -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
diff --git a/src/Reflex/FastWeak.hs b/src/Reflex/FastWeak.hs
--- a/src/Reflex/FastWeak.hs
+++ b/src/Reflex/FastWeak.hs
@@ -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
diff --git a/src/Reflex/FunctorMaybe.hs b/src/Reflex/FunctorMaybe.hs
--- a/src/Reflex/FunctorMaybe.hs
+++ b/src/Reflex/FunctorMaybe.hs
@@ -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
diff --git a/src/Reflex/Network.hs b/src/Reflex/Network.hs
--- a/src/Reflex/Network.hs
+++ b/src/Reflex/Network.hs
@@ -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)
diff --git a/src/Reflex/NotReady/Class.hs b/src/Reflex/NotReady/Class.hs
--- a/src/Reflex/NotReady/Class.hs
+++ b/src/Reflex/NotReady/Class.hs
@@ -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
 
diff --git a/src/Reflex/Optimizer.hs b/src/Reflex/Optimizer.hs
--- a/src/Reflex/Optimizer.hs
+++ b/src/Reflex/Optimizer.hs
@@ -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
diff --git a/src/Reflex/Patch.hs b/src/Reflex/Patch.hs
--- a/src/Reflex/Patch.hs
+++ b/src/Reflex/Patch.hs
@@ -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)
diff --git a/src/Reflex/Patch/Class.hs b/src/Reflex/Patch/Class.hs
--- a/src/Reflex/Patch/Class.hs
+++ b/src/Reflex/Patch/Class.hs
@@ -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.
 --
diff --git a/src/Reflex/Profiled.hs b/src/Reflex/Profiled.hs
--- a/src/Reflex/Profiled.hs
+++ b/src/Reflex/Profiled.hs
@@ -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)
diff --git a/src/Reflex/Pure.hs b/src/Reflex/Pure.hs
--- a/src/Reflex/Pure.hs
+++ b/src/Reflex/Pure.hs
@@ -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 (..)
diff --git a/src/Reflex/Query/Base.hs b/src/Reflex/Query/Base.hs
--- a/src/Reflex/Query/Base.hs
+++ b/src/Reflex/Query/Base.hs
@@ -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
diff --git a/src/Reflex/Query/Class.hs b/src/Reflex/Query/Class.hs
--- a/src/Reflex/Query/Class.hs
+++ b/src/Reflex/Query/Class.hs
@@ -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
diff --git a/src/Reflex/Requester/Base.hs b/src/Reflex/Requester/Base.hs
--- a/src/Reflex/Requester/Base.hs
+++ b/src/Reflex/Requester/Base.hs
@@ -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
+            )
diff --git a/src/Reflex/Spider.hs b/src/Reflex/Spider.hs
--- a/src/Reflex/Spider.hs
+++ b/src/Reflex/Spider.hs
@@ -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
diff --git a/src/Reflex/Spider/Internal.hs b/src/Reflex/Spider/Internal.hs
--- a/src/Reflex/Spider/Internal.hs
+++ b/src/Reflex/Spider/Internal.hs
@@ -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)
diff --git a/src/Reflex/Time.hs b/src/Reflex/Time.hs
--- a/src/Reflex/Time.hs
+++ b/src/Reflex/Time.hs
@@ -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
-
diff --git a/src/Reflex/Workflow.hs b/src/Reflex/Workflow.hs
--- a/src/Reflex/Workflow.hs
+++ b/src/Reflex/Workflow.hs
@@ -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)
diff --git a/test/RequesterT.hs b/test/RequesterT.hs
--- a/test/RequesterT.hs
+++ b/test/RequesterT.hs
@@ -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
