diff --git a/bench/Main.hs b/bench/Main.hs
new file mode 100644
--- /dev/null
+++ b/bench/Main.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+module Main where
+
+import Data.Functor.Misc
+import Control.Monad.Primitive
+import Control.Monad.IO.Class
+import Data.Dependent.Sum
+import Control.Concurrent.STM
+import Control.Applicative
+import System.IO.Unsafe
+import Data.IORef
+import Control.DeepSeq
+import Control.Exception (evaluate)
+import Control.Monad
+import Reflex
+import Reflex.Host.Class
+import System.Mem
+import System.IO
+import Criterion.Main
+
+import qualified Data.Traversable as T
+
+import qualified Data.Dependent.Map as DM
+
+
+main :: IO ()
+main = defaultMain
+  [ bgroup "micro" micros ]
+
+instance NFData (IORef a) where
+  rnf x = seq x ()
+
+instance NFData (TVar a) where
+  rnf x = seq x ()
+
+newtype WHNF a = WHNF a
+instance NFData (WHNF a) where
+  rnf (WHNF a) = seq a ()
+
+withSetup :: NFData b => String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
+withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
+  bench name . nfIO $ runSpiderHost (action a)
+
+withSetupWHNF :: String -> SpiderHost a -> (a -> SpiderHost b) -> Benchmark
+withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
+  bench name . whnfIO $ runSpiderHost (action a)
+
+
+micros :: [Benchmark]
+micros =
+  [ bench "newIORef" $ whnfIO $ void $ newIORef ()
+  , env (newIORef (42 :: Int)) (bench "readIORef" . whnfIO . readIORef)
+  , bench "newTVar" $ whnfIO $ void $ newTVarIO ()
+  , env (newTVarIO (42 :: Int)) (bench "readTVar" . whnfIO . readTVarIO)
+  , bench "newEventWithTrigger" $ whnfIO . void $ runSpiderHost $ newEventWithTrigger $
+      \trigger -> return () <$ evaluate trigger
+  , bench "newEventWithTriggerRef" $ whnfIO . void $ runSpiderHost newEventWithTriggerRef
+  , withSetupWHNF "subscribeEvent" newEventWithTriggerRef $ subscribeEvent . fst
+  , withSetupWHNF "subscribeSwitch"
+    (join $ hold <$> fmap fst newEventWithTriggerRef <*> fmap fst newEventWithTriggerRef)
+    (subscribeEvent . switch)
+  , withSetupWHNF "subscribeMerge(1)" (setupMerge 1) $ \(ev,_) -> subscribeEvent ev
+  , withSetupWHNF "subscribeMerge(100)" (setupMerge 100) (subscribeEvent . fst)
+  , withSetupWHNF "subscribeMerge(10000)" (setupMerge 10000) (subscribeEvent . fst)
+  , bench "runHostFrame" $ whnfIO $ runSpiderHost $ runHostFrame $ return ()
+  , withSetupWHNF "fireEventsAndRead(single/single)"
+    (newEventWithTriggerRef >>= subscribePair)
+    (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
+  , withSetupWHNF "fireEventsOnly"
+    (newEventWithTriggerRef >>= subscribePair)
+    (\(subd, trigger) -> do
+        Just key <- liftIO $ readIORef trigger
+        fireEvents [key :=> (42 :: Int)])
+  , withSetupWHNF "fireEventsAndRead(head/merge1)"
+    (setupMerge 1 >>= subscribePair)
+    (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
+  , withSetupWHNF "fireEventsAndRead(head/merge100)"
+    (setupMerge 100 >>= subscribePair)
+    (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
+  , withSetupWHNF "fireEventsAndRead(head/merge10000)"
+      (setupMerge 10000 >>= subscribePair)
+      (\(subd, t:riggers) -> fireAndRead t (42 :: Int) subd)
+  , withSetupWHNF "fireEventsOnly(head/merge100)"
+    (setupMerge 100 >>= subscribePair)
+    (\(subd, t:riggers) -> do
+        Just key <- liftIO $ readIORef t
+        fireEvents [key :=> (42 :: Int)])
+  , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev,trigger) -> hold (42 :: Int) ev
+  , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample    
+  ]
+
+setupMerge :: Int
+           -> SpiderHost (Event Spider (DM.DMap (Const2 Int a)),
+                         [IORef (Maybe (EventTrigger Spider a))])
+setupMerge num = do
+  (evs, triggers) <- unzip <$> replicateM 100 newEventWithTriggerRef
+  let !m = DM.fromList [WrapArg (Const2 i) :=> v | (i,v) <- zip [0..] evs]
+  pure (merge m, triggers)
+
+subscribePair :: (Event Spider a, b) -> SpiderHost (EventHandle Spider a, b)
+subscribePair (ev, b) = (,b) <$> subscribeEvent ev
+
+fireAndRead :: IORef (Maybe (EventTrigger Spider a)) -> a -> EventHandle Spider b
+            -> SpiderHost (Maybe b)
+fireAndRead trigger val subd = do
+  Just key <- liftIO $ readIORef trigger
+  fireEventsAndRead [key :=> val] $ readEvent subd >>= T.sequence
diff --git a/reflex.cabal b/reflex.cabal
--- a/reflex.cabal
+++ b/reflex.cabal
@@ -1,5 +1,5 @@
 Name: reflex
-Version: 0.1.1
+Version: 0.2
 Synopsis: Higher-order Functional Reactive Programming
 Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
 License: BSD3
@@ -20,11 +20,12 @@
     dependent-sum == 0.2.*,
     dependent-map == 0.1.*,
     semigroups == 0.16.*,
-    mtl == 2.2.*,
+    mtl >= 2.1 && < 2.3,
     containers == 0.5.*,
     these == 0.4.*,
     primitive == 0.5.*,
-    template-haskell >= 2.9 && < 2.11
+    template-haskell >= 2.9 && < 2.11,
+    ref-tf == 0.4.*
 
   exposed-modules:
     Reflex,
@@ -34,12 +35,28 @@
     Reflex.Dynamic,
     Reflex.Dynamic.TH,
     Reflex.Host.Class,
-    Data.Functor.Misc,
-    Control.Monad.Ref
+    Data.Functor.Misc
 
   other-extensions: TemplateHaskell
   ghc-prof-options: -fprof-auto
   ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
+
+benchmark spider-bench
+  type: exitcode-stdio-1.0
+  hs-source-dirs: bench
+  main-is: Main.hs
+  ghc-options: -O2 -rtsopts
+  build-depends:
+    base,
+    dependent-sum,
+    dependent-map,
+    transformers >= 0.3 && < 0.5,
+    stm == 2.4.*,
+    deepseq >= 1.3 && < 1.5,
+    mtl,
+    primitive,
+    criterion == 1.1.*,
+    reflex
 
 source-repository head
   type: git
diff --git a/src/Control/Monad/Ref.hs b/src/Control/Monad/Ref.hs
deleted file mode 100644
--- a/src/Control/Monad/Ref.hs
+++ /dev/null
@@ -1,85 +0,0 @@
-{-# LANGUAGE RecursiveDo, TypeFamilies, LambdaCase #-}
-module Control.Monad.Ref where
-
-import Control.Monad
-import Control.Monad.Trans
-import Control.Monad.Reader
-import Control.Monad.Writer
-import Data.IORef
-
-class Monad m => MonadRef m where
-  type Ref m :: * -> *
-  newRef :: a -> m (Ref m a)
-  readRef :: Ref m a -> m a
-  writeRef :: Ref m a -> a -> m ()
-  atomicModifyRef :: Ref m a -> (a -> (a, b)) -> m b
-
-instance MonadRef IO where
-  type Ref IO = IORef
-  {-# INLINE newRef #-}
-  newRef = newIORef
-  {-# INLINE readRef #-}
-  readRef = readIORef
-  {-# INLINE writeRef #-}
-  writeRef = writeIORef
-  {-# INLINE atomicModifyRef #-}
-  atomicModifyRef r f = do
-    result <- atomicModifyIORef' r f
---    evaluate =<< readIORef r --TODO: Verify that ghcjs now strictly evaluates the values in atomicModifyIORef', then remove this line
-    return result
-
-{-# INLINE cacheM #-}
-cacheM :: (MonadRef m, MonadRef m', Ref m ~ Ref m') => m' a -> m (m' a, m ())
-cacheM a = do
-  r <- newRef undefined
-  let invalidate = writeRef r $ do
-        result <- a
-        writeRef r $ return result
-        return result
-  invalidate
-  return (join $ readRef r, invalidate)
-
-{-# INLINE cacheMWithTry #-}
-cacheMWithTry :: (MonadRef m, MonadRef m', Ref m ~ Ref m') => m' a -> m (m' a, m (Maybe a), m ())
-cacheMWithTry a = do
-  r <- newRef $ Left a
-  let invalidate = writeRef r $ Left a
-      get = readRef r >>= \case
-        Left a' -> do
-          result <- a'
-          writeRef r $ Right result
-          return result
-        Right result -> return result
-      tryGet = readRef r >>= \case
-        Left _ -> return Nothing
-        Right result -> return $ Just result
-  return (get, tryGet, invalidate)
-
--- | Not thread-safe or reentrant
-{-# INLINE memoM #-}
-memoM :: (MonadRef m, MonadRef m', Ref m ~ Ref m') => m' a -> m (m' a)
-memoM = liftM fst . cacheM
-
-{-# INLINE replaceRef #-}
-replaceRef :: MonadRef m => Ref m a -> a -> m a
-replaceRef r new = atomicModifyRef r $ \old -> (new, old)
-
-{-# INLINE modifyRef #-}
-modifyRef :: MonadRef m => Ref m a -> (a -> a) -> m ()
-modifyRef r f = atomicModifyRef r $ \a -> (f a, ())
-
-instance (Monoid w, MonadRef m) => MonadRef (WriterT w m) where
-  {-# SPECIALIZE instance Monoid w => MonadRef (WriterT w IO) #-}
-  type Ref (WriterT w m) = Ref m
-  newRef = lift . newRef
-  readRef = lift . readRef
-  writeRef r = lift . writeRef r
-  atomicModifyRef r f = lift $ atomicModifyRef r f
-
-instance MonadRef m => MonadRef (ReaderT r m) where
-  {-# SPECIALIZE instance MonadRef (ReaderT r IO) #-}
-  type Ref (ReaderT r m) = Ref m
-  newRef = lift . newRef
-  readRef = lift . readRef
-  writeRef r = lift . writeRef r
-  atomicModifyRef r f = lift $ atomicModifyRef r f
diff --git a/src/Reflex/Class.hs b/src/Reflex/Class.hs
--- a/src/Reflex/Class.hs
+++ b/src/Reflex/Class.hs
@@ -70,9 +70,12 @@
 -- Convenience functions
 --------------------------------------------------------------------------------
 
+-- | Create an Event from another Event.
+-- The provided function can sample 'Behavior's and hold 'Event's.
 pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
 pushAlways f e = push (liftM Just . f) e
 
+-- | Flipped version of 'fmap'.
 ffor :: Functor f => f a -> (a -> b) -> f b
 ffor = flip fmap
 
@@ -80,12 +83,17 @@
   fmap f = pull . liftM f . sample
 
 --TODO: See if there's a better class in the standard libraries already
+-- | A class for values that combines filtering and mapping using 'Maybe'.
 class FunctorMaybe f where
+  -- | Combined mapping and filtering function.
   fmapMaybe :: (a -> Maybe b) -> f a -> f b
 
+-- | Flipped version of 'fmapMaybe'.
 fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
 fforMaybe = flip fmapMaybe
 
+-- | 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
 
@@ -95,6 +103,9 @@
 instance Reflex t => Functor (Event t) where
   fmap f = fmapMaybe $ Just . f
 
+-- | Create a new 'Event' by combining each occurence with the next value
+-- of the list using the supplied function. If the list runs out of items,
+-- all subsequent 'Event' occurrences will be ignored.
 zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
 zipListWithEvent f l e = do
   rec lb <- hold l eTail
@@ -107,22 +118,33 @@
       lb `seq` eBoth `seq` eTail `seq` return ()
   return $ fmap fst eBoth
 
--- | Replace the occurrence value of the Event with the value of the Behavior at the time of the occurrence
+-- | Replace each occurrence value of the 'Event' with the value of the
+-- 'Behavior' at the time of that occurrence.
 tag :: Reflex t => Behavior t b -> Event t a -> Event t b
 tag b = pushAlways $ \_ -> sample b
 
-attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
-attachWithMaybe f b e = flip push e $ \o -> liftM (flip f o) $ sample b
+-- | Create a new 'Event' that combines occurences of supplied 'Event'
+-- with the current value of the 'Behavior'.
+attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
+attach = attachWith (,)
 
+-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
+-- by combining it with the current value of the 'Behavior'.
 attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
 attachWith f = attachWithMaybe $ \a b -> Just $ f a b
 
-attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
-attach = attachWith (,)
+-- | Create a new 'Event' by combining each occurence with the current
+-- value of the 'Behavior'. The occurrence is discarded if the combining function
+-- returns Nothing
+attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
+attachWithMaybe f b e = flip push e $ \o -> liftM (flip f o) $ sample b
 
+-- | Alias for 'headE'
 onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
 onceE = headE
 
+-- | Create a new 'Event' that only occurs on the first occurence of
+-- the supplied 'Event'.
 headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
 headE e = do
   rec be <- hold e $ fmap (const never) e'
@@ -130,24 +152,42 @@
       e' `seq` return ()
   return e'
 
+-- | Create a new 'Event' that occurs on all but the first occurence
+-- of the supplied 'Event'.
 tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
 tailE e = liftM snd $ headTailE e
 
+-- | Create a tuple of two 'Event's with the first one occuring only
+-- the first time the supplied 'Event' occurs and the second occuring
+-- on all but the first occurence.
 headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
 headTailE e = do
   eHead <- headE e
   be <- hold never $ fmap (const e) eHead
   return (eHead, switch be)
 
+-- | Split the supplied 'Event' into two individual 'Event's occuring
+-- at the same time with the respective values from the tuple.
 splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
 splitE e = (fmap fst e, fmap snd e)
 
+-- | Print the supplied 'String' and the value of the 'Event' on each
+-- occurence. This should /only/ be used for debugging.
+--
+-- Note: As with Debug.Trace.trace, the message will only be printed if
+-- the 'Event' is actually used.
 traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
 traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
 
+-- | Print the output of the supplied function on each occurence of
+-- the 'Event'. This should /only/ be used for debugging.
+--
+-- Note: As with Debug.Trace.trace, the message will only be printed if
+-- the 'Event' is actually used.
 traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
 traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
 
+-- | Tag type for 'Either' to use it as a 'DSum'.
 data EitherTag l r a where
   LeftTag :: EitherTag l r l
   RightTag :: EitherTag l r r
@@ -175,16 +215,19 @@
     LeftTag -> showsPrec n a
     RightTag -> showsPrec n a
 
+-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
 eitherToDSum :: Either a b -> DSum (EitherTag a b)
 eitherToDSum = \case
   Left a -> LeftTag :=> a
   Right b -> RightTag :=> b
 
+-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
 dsumToEither :: DSum (EitherTag a b) -> Either a b
 dsumToEither = \case
   LeftTag :=> a -> Left a
   RightTag :=> b -> Right b
 
+-- | Extract the values of a 'DMap' of 'EitherTag's.
 dmapToThese :: DMap (EitherTag a b) -> Maybe (These a b)
 dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
   (Nothing, Nothing) -> Nothing
@@ -192,9 +235,13 @@
   (Nothing, Just b) -> Just $ That b
   (Just a, Just b) -> Just $ These a b
 
+-- | 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'.
 appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
 appendEvents e1 e2 = fmap (mergeThese mappend) $ align e1 e2
 
+{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
 sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
 sequenceThese t = case t of
   This ma -> liftM This ma
@@ -206,19 +253,33 @@
   mappend a b = mconcat [a, b]
   mconcat = fmap sconcat . mergeList
 
+-- | Create a new 'Event' that occurs if at least one of the 'Event's
+-- in the list occurs. If multiple occur at the same time they are
+-- folded from the left with the given function.
 mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
 mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> v) -> v) . DMap.toList) $ merge $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
 
+-- | Create a new 'Event' that occurs if at least one of the 'Event's
+-- in the list occurs. If multiple occur at the same time the value is
+-- the value of the leftmost event.
 leftmost :: Reflex t => [Event t a] -> Event t a
 leftmost = mergeWith const
 
+-- | Create a new 'Event' that occurs if at least one of the 'Event's
+-- in the list occurs and has a list of the values of all 'Event's
+-- occuring at that time.
 mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
 mergeList [] = never
 mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
 
+-- | Create a new 'Event' combining the map of 'Event's into an
+-- 'Event' that occurs if at least one of them occurs and has a map of
+-- values of all 'Event's occuring at that time.
 mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
 mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
 
+-- | Split the event into an 'EventSelector' that allows efficient
+-- selection of the individual 'Event's.
 fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
 fanMap = fan . fmap mapToDMap
 
@@ -234,5 +295,7 @@
   nil = never
   align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [WrapArg LeftTag :=> ea, WrapArg RightTag :=> eb]
 
+-- | Create a new 'Event' that only occurs if the supplied 'Event'
+-- occurs and the 'Behavior' is true at the time of occurence.
 gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
 gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
diff --git a/src/Reflex/Dynamic.hs b/src/Reflex/Dynamic.hs
--- a/src/Reflex/Dynamic.hs
+++ b/src/Reflex/Dynamic.hs
@@ -88,27 +88,36 @@
       => HBuild' l (a->r) where
   hBuild' l x = hBuild' (HCons x l)
 
-
+-- | A container for a value that can change over time and allows notifications on changes.
+-- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
+-- change if and only if the Event fires.
 data Dynamic t a
   = Dynamic (Behavior t a) (Event t a)
 
 unsafeDynamic :: Behavior t a -> Event t a -> Dynamic t a
 unsafeDynamic = Dynamic
 
+-- | Extract the 'Behavior' of a 'Dynamic'.
 current :: Dynamic t a -> Behavior t a
 current (Dynamic b _) = b
 
+-- | Extract the 'Event' of the 'Dynamic'.
 updated :: Dynamic t a -> Event t a
 updated (Dynamic _ e) = e
 
+-- | 'Dynamic' with the constant supplied value.
 constDyn :: Reflex t => a -> Dynamic t a
 constDyn x = Dynamic (constant x) never
 
+-- | Create a 'Dynamic' using the initial value that changes every
+-- time the 'Event' occurs.
 holdDyn :: MonadHold t m => a -> Event t a -> m (Dynamic t a)
 holdDyn v0 e = do
   b <- hold v0 e
   return $ Dynamic b e
 
+-- | Create a new 'Dynamic' that only signals changes if the values
+-- actually changed.
 nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
 nubDyn d =
   let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
@@ -123,12 +132,16 @@
     
 -}      
 
+-- | Map a function over a 'Dynamic'.
 mapDyn :: (Reflex t, MonadHold t m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
 mapDyn f = mapDynM $ return . f
 
+-- | Flipped version of 'mapDyn'.
 forDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
 forDyn = flip mapDyn
 
+-- | Map a monadic function over a 'Dynamic'.  The only monadic action that the given function can
+-- perform is 'sample'.
 {-# INLINE mapDynM #-}
 mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b)
 mapDynM f d = do
@@ -139,9 +152,15 @@
   let b' = pull $ sample =<< sample bb'
   return $ Dynamic b' e'
 
+-- | Create a 'Dynamic' using the initial value and change it each
+-- time the 'Event' occurs using a folding function on the previous
+-- value and the value of the 'Event'.
 foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
 foldDyn f = foldDynM (\o v -> return $ f o v)
 
+-- | Create a 'Dynamic' using the initial value and change it each
+-- time the 'Event' occurs using a monadic folding function on the
+-- previous value and the value of the 'Event'.
 foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
 foldDynM f z e = do
   rec let e' = flip push e $ \o -> do
@@ -150,13 +169,18 @@
       b' <- hold z e'
   return $ Dynamic b' e'
 
+-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
 count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
 count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
 
+-- | Create a new 'Dynamic' using the initial value that flips its
+-- value every time the 'Event' occurs.
 toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
 toggle = foldDyn (const not)
 
--- | Switches to the new event whenever it receives one.  Switching occurs *before* occurring the inner event.
+-- | Switches to the new 'Event' whenever it receives one.  Switching
+-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
+-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
 switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
 switchPromptlyDyn de =
   let eLag = switch $ current de
@@ -182,14 +206,19 @@
 mconcatE = concatEventsWith mappend
 -}
 
+-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
+-- respective value of the tuple.
 splitDyn :: (Reflex t, MonadHold t m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
 splitDyn d = liftM2 (,) (mapDyn fst d) (mapDyn snd d)
 
+-- | Merge the 'Dynamic' values using their 'Monoid' instance.
 mconcatDyn :: forall t m a. (Reflex t, MonadHold t m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
 mconcatDyn es = do
   ddm :: Dynamic t (DMap (Const2 Int a)) <- distributeDMapOverDyn $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
   mapDyn (mconcat . map (\(Const2 _ :=> v) -> v) . DMap.toList) ddm
 
+-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of
+-- Dynamic values.
 distributeDMapOverDyn :: forall t m k. (Reflex t, MonadHold t m, GCompare k) => DMap (WrapArg (Dynamic t) k) -> m (Dynamic t (DMap k))
 distributeDMapOverDyn dm = case DMap.toList dm of
   [] -> return $ constDyn DMap.empty
@@ -208,6 +237,9 @@
     let bdm = pull $ sample =<< sample bbdm
     return $ Dynamic bdm edm
 
+-- | 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.
 combineDyn :: forall t m a b c. (Reflex t, MonadHold t m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
 combineDyn f da db = do
   let eab = align (updated da) (updated db)
@@ -234,6 +266,8 @@
   in leftmost [eFast, eSlow]
 -}
 
+-- | Join a nested 'Dynamic' into a new 'Dynamic' that has the value
+-- of the inner 'Dynamic'.
 joinDyn :: forall t a. (Reflex t) => Dynamic t (Dynamic t a) -> Dynamic t a
 joinDyn dd =
   let b' = pull $ sample . current =<< sample (current dd)
@@ -244,6 +278,8 @@
   in Dynamic b' e'
 
 --TODO: Generalize this to functors other than Maps
+-- | 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 dd =
   let b' = pull $ mapM (sample . current) =<< sample (current dd)
@@ -258,23 +294,57 @@
       e' = leftmost [eBoth, eOuter, eInner]
   in Dynamic b' e'
 
+-- | Print the value of the 'Dynamic' on each change and prefix it
+-- with the provided string. This should /only/ be used for debugging.
+--
+-- Note: Just like Debug.Trace.trace, the value will only be shown if something
+-- else in the system is depending on it.
 traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
 traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
 
+-- | Print the result of applying the provided function to the value
+-- of the 'Dynamic' on each change. This should /only/ be used for
+-- debugging.
+--
+-- Note: Just like Debug.Trace.trace, the value will only be shown if something
+-- else in the system is depending on it.
 traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
 traceDynWith f d =
   let e' = traceEventWith f $ updated d
   in Dynamic (current d) e'
 
+-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
+-- each time the 'Event' occurs.
+--
+-- Note: `tagDyn d e` differs from `tag (current d) e` in the case that `e` is firing
+-- at the same time that `d` is changing.  With `tagDyn d e`, the *new* value of `d`
+-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
+-- will be used, since the 'Behavior' won't be updated until the end of the frame.
+-- Additionally, this means that the output 'Event' may not be used to directly change
+-- the input 'Dynamic', because that would mean its value depends on itself.  When creating
+-- cyclic data flows, generally `tag (current d) e` is preferred.
 tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
 tagDyn = attachDynWith const
 
+-- | Attach the current value of the 'Dynamic' to the value of the
+-- 'Event' each time it occurs.
+--
+-- Note: `attachDyn d` is not the same as `attach (current d)`.  See 'tagDyn' for details.
 attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
 attachDyn = attachDynWith (,)
 
+-- | Combine the current value of the 'Dynamic' with the value of the
+-- 'Event' each time it occurs.
+--
+-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`.  See 'tagDyn' for details.
 attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
 attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
 
+-- | Create a new 'Event' by combining the value at each occurence
+-- with the current value of the 'Dynamic' value and possibly
+-- filtering if the combining function returns 'Nothing'.
+--
+-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`.  See 'tagDyn' for details.
 attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
 attachDynWithMaybe f d e =
   let e' = attach (current d) e
@@ -287,14 +357,25 @@
 -- Demux
 --------------------------------------------------------------------------------
 
+-- | Represents a time changing value together with an 'EventSelector'
+-- that can efficiently detect when the underlying Dynamic has a particular value.
+-- This is useful for representing data like the current selection of a long list.
+--
+-- Semantically,
+-- > getDemuxed (demux d) k === mapDyn (== k) d
+-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
+-- rather than /O(n)/ for mapDyn.
 data Demux t k = Demux { demuxValue :: Behavior t k
                        , demuxSelector :: EventSelector t (Const2 k Bool)
                        }
 
+-- | Demultiplex an input value to a 'Demux' with many outputs.  At any given time, whichever output is indicated by the given 'Dynamic' will be 'True'.
 demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
 demux k = Demux (current k) (fan $ attachWith (\k0 k1 -> if k0 == k1 then DMap.empty else DMap.fromList [Const2 k0 :=> False, Const2 k1 :=> True]) (current k) (updated k))
 
 --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
+-- | Select a particular output of the 'Demux'; this is equivalent to (but much faster than)
+-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
 getDemuxed :: (Reflex t, MonadHold t m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
 getDemuxed d k = do
   let e = select (demuxSelector d) (Const2 k)
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
@@ -1,4 +1,4 @@
-{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TypeOperators, GADTs, EmptyDataDecls #-}
+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TypeOperators, GADTs, EmptyDataDecls, PatternGuards #-}
 module Reflex.Dynamic.TH (qDyn, unqDyn) where
 
 import Reflex.Dynamic
diff --git a/src/Reflex/Host/Class.hs b/src/Reflex/Host/Class.hs
--- a/src/Reflex/Host/Class.hs
+++ b/src/Reflex/Host/Class.hs
@@ -19,6 +19,13 @@
   readEvent :: EventHandle t a -> m (Maybe (m a))
 
 class (Monad m, ReflexHost t) => MonadReflexCreateTrigger t m | m -> t where
+  -- | Creates an original Event (one that is not based on any other event).
+  -- When a subscriber first subscribes to an event (building another event
+  -- that depends on the subscription) the given callback function is run by
+  -- passing a trigger. The event is then set up in IO. The callback
+  -- function returns an accompanying teardown action.
+  -- Any time between setup and teardown the trigger can be used to fire
+  -- the event.
   newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
 
 class (Monad m, ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexHost t m | m -> t where
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
@@ -13,7 +13,9 @@
 import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
 import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
 import GHC.Exts
+#if !MIN_VERSION_base(4,8,0)
 import Control.Applicative
+#endif
 import Data.Dependent.Map (DMap, DSum (..))
 import qualified Data.Dependent.Map as DMap
 import Data.GADT.Compare
@@ -164,7 +166,7 @@
           , holdNodeId = unsafeNodeId (v0, e)
 #endif
           }
-        !s = SubscriberHold h
+    s <- newSubscriberHold h
     writeIORef subscriberRef $ unsafeCoerce s
     modifyIORef' holdInitRef (SomeHoldInit e h :)
     return $ BehaviorHold h
@@ -277,7 +279,7 @@
 data FanSubscribed k
    = FanSubscribed { fanSubscribedSubscribers :: !(IORef (DMap (FanSubscriberKey k)))
                    , fanSubscribedParent :: !(EventSubscribed (DMap k))
-                   , fanSubscribedSelf :: !(Subscriber (DMap k))
+                   , fanSubscribedSelf :: {-# NOUNPACK #-} (Subscriber (DMap k))
 #ifdef DEBUG_NODEIDS
                    , fanSubscribedNodeId :: Int
 #endif
@@ -292,9 +294,9 @@
    = SwitchSubscribed { switchSubscribedOccurrence :: !(IORef (Maybe a))
                       , switchSubscribedHeight :: !(IORef Int)
                       , switchSubscribedSubscribers :: !(IORef [WeakSubscriber a])
-                      , switchSubscribedSelf :: !(Subscriber a)
+                      , switchSubscribedSelf :: {-# NOUNPACK #-} (Subscriber a)
                       , switchSubscribedSelfWeak :: !(IORef (Weak (Subscriber a)))
-                      , switchSubscribedOwnInvalidator :: !Invalidator
+                      , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} Invalidator
                       , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
                       , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
                       , switchSubscribedParent :: !(Behavior (Event a))
@@ -313,7 +315,7 @@
    = CoincidenceSubscribed { coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
                            , coincidenceSubscribedSubscribers :: !(IORef [WeakSubscriber a])
                            , coincidenceSubscribedHeight :: !(IORef Int)
-                           , coincidenceSubscribedOuter :: !(Subscriber (Event a))
+                           , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
                            , coincidenceSubscribedOuterParent :: !(EventSubscribed (Event a))
                            , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
 #ifdef DEBUG_NODEIDS
@@ -390,6 +392,59 @@
    | EventSubscribedSwitch !(SwitchSubscribed a)
    | EventSubscribedCoincidence !(CoincidenceSubscribed a)
 
+-- These function are constructor functions that are marked NOINLINE so they are
+-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
+-- so any weak references that are attached to the constructors will have their
+-- finalizer run. Using the opaque constructor, does not see the
+-- constructor application, so it behaves like an IORef and cannot be fused away.
+--
+-- The result is also evaluated to WHNF, since forcing a thunk invalidates
+-- the weak pointer to it in some cases.
+
+{-# NOINLINE newRootSubscribed #-}
+newRootSubscribed :: IORef (Maybe a) -> IORef [WeakSubscriber a] -> IO (RootSubscribed a)
+newRootSubscribed occ subs =
+  return $! RootSubscribed
+    { rootSubscribedOccurrence = occ
+    , rootSubscribedSubscribers = subs
+    }
+
+{-# NOINLINE newSubscriberPush #-}
+newSubscriberPush :: (a -> EventM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
+newSubscriberPush compute subd = return $! SubscriberPush compute subd
+
+{-# NOINLINE newSubscriberHold #-}
+newSubscriberHold :: Hold a -> IO (Subscriber a)
+newSubscriberHold h = return $! SubscriberHold h
+
+{-# NOINLINE newSubscriberFan #-}
+newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k))
+newSubscriberFan subd = return $! SubscriberFan subd
+
+{-# NOINLINE newSubscriberSwitch #-}
+newSubscriberSwitch :: SwitchSubscribed a -> IO (Subscriber a)
+newSubscriberSwitch subd = return $! SubscriberSwitch subd
+
+{-# NOINLINE newSubscriberCoincidenceOuter #-}
+newSubscriberCoincidenceOuter :: CoincidenceSubscribed b -> IO (Subscriber (Event b))
+newSubscriberCoincidenceOuter subd = return $! SubscriberCoincidenceOuter subd
+
+{-# NOINLINE newSubscriberCoincidenceInner #-}
+newSubscriberCoincidenceInner :: CoincidenceSubscribed a -> IO (Subscriber a)
+newSubscriberCoincidenceInner subd = return $! SubscriberCoincidenceInner subd
+
+{-# NOINLINE newInvalidatorSwitch #-}
+newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
+newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
+
+{-# NOINLINE newInvalidatorPull #-}
+newInvalidatorPull :: Pull a -> IO Invalidator
+newInvalidatorPull p = return $! InvalidatorPull p
+
+{-# NOINLINE newBox #-}
+newBox :: a -> IO (Box a)
+newBox a = return $! Box a
+
 --type role Behavior representational
 data Behavior a
    = BehaviorHold !(Hold a)
@@ -605,7 +660,7 @@
 
 subscribeCoincidenceInner :: Event a -> Int -> CoincidenceSubscribed a -> EventM (Maybe a, Int, EventSubscribed a)
 subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
-  let !subInner = SubscriberCoincidenceInner subscribedUnsafe
+  subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
   wsubInner <- liftIO $ mkWeakPtrWithDebug subInner "SubscriberCoincidenceInner"
   innerSubd <- {-# SCC "innerSubd" #-} (subscribe o $ WeakSubscriberSimple wsubInner)
   innerOcc <- liftIO $ getEventSubscribedOcc innerSubd
@@ -644,7 +699,7 @@
         liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
         return $ pullSubscribedValue subscribed
       Nothing -> do
-        let !i = InvalidatorPull p
+        i <- liftIO $ newInvalidatorPull p
         wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
         parentsRef <- liftIO $ newIORef []
         a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) $ Just (wi, parentsRef)
@@ -776,10 +831,7 @@
     Just subscribed -> return subscribed
     Nothing -> liftIO $ do
       subscribersRef <- newIORef []
-      let !subscribed = RootSubscribed
-            { rootSubscribedOccurrence = rootOccurrence r
-            , rootSubscribedSubscribers = subscribersRef
-            }
+      subscribed <- newRootSubscribed (rootOccurrence r) subscribersRef
       -- Strangely, init needs the same stuff as a RootSubscribed has, but it must not be the same as the one that everyone's subscribing to, or it'll leak memory
       uninit <- rootInit r $ RootTrigger (subscribersRef, rootOccurrence r)
       addFinalizer subscribed $ do
@@ -797,7 +849,7 @@
     Just subscribed -> return subscribed
     Nothing -> do -- Not yet subscribed
       subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ pushSubscribed p
-      let !s = SubscriberPush (pushCompute p) subscribedUnsafe
+      s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
       ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberPush"
       subd <- subscribe (pushParent p) $ WeakSubscriberSimple ws
       parentOcc <- liftIO $ getEventSubscribedOcc subd
@@ -826,7 +878,7 @@
     Nothing -> if DMap.null $ mergeParents m then emptyMergeSubscribed else do
       subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
       subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
-      let !s = Box subscribedUnsafe
+      s <- liftIO $ newBox subscribedUnsafe
       ws <- liftIO $ mkWeakPtrWithDebug s "SubscriberMerge"
       subscribers :: [(Any, Maybe (DSum k), Int, DSum (WrapArg EventSubscribed k))] <- forM (DMap.toList $ mergeParents m) $ {-# SCC "getMergeSubscribed.a" #-} \(WrapArg k :=> e) -> {-# SCC "getMergeSubscribed.a1" #-} do
         parentSubd <- {-# SCC "getMergeSubscribed.a.parentSubd" #-} subscribe e $ WeakSubscriberMerge k ws
@@ -886,7 +938,7 @@
     Just subscribed -> return subscribed
     Nothing -> do
       subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ liftM fromJust $ readIORef $ fanSubscribed f
-      let !sub = SubscriberFan subscribedUnsafe
+      sub <- liftIO $ newSubscriberFan subscribedUnsafe
       wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberFan"
       subd <- subscribe (fanParent f) $ WeakSubscriberSimple wsub
       subscribersRef <- liftIO $ newIORef DMap.empty
@@ -909,8 +961,8 @@
     Nothing -> do
       subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
       subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
-      let !i = InvalidatorSwitch subscribedUnsafe
-          !sub = SubscriberSwitch subscribedUnsafe
+      i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
+      sub <- liftIO $ newSubscriberSwitch subscribedUnsafe
       wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
       wiRef <- liftIO $ newIORef wi
       wsub <- liftIO $ mkWeakPtrWithDebug sub "SubscriberSwitch"
@@ -951,7 +1003,7 @@
     Nothing -> do
       subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
       subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
-      let !subOuter = SubscriberCoincidenceOuter subscribedUnsafe
+      subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
       wsubOuter <- liftIO $ mkWeakPtrWithDebug subOuter "subOuter"
       outerSubd <- subscribe (coincidenceParent c) $ WeakSubscriberSimple wsubOuter
       outerOcc <- liftIO $ getEventSubscribedOcc outerSubd
@@ -1287,10 +1339,12 @@
   {-# INLINE newRef #-}
   {-# INLINE readRef #-}
   {-# INLINE writeRef #-}
-  {-# INLINE atomicModifyRef #-}
   newRef = liftIO . newRef
   readRef = liftIO . readRef
   writeRef r a = liftIO $ writeRef r a
+
+instance MonadAtomicRef EventM where
+  {-# INLINE atomicModifyRef #-}
   atomicModifyRef r f = liftIO $ atomicModifyRef r f
 
 newtype SpiderHost a = SpiderHost { runSpiderHost :: IO a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO)
@@ -1334,6 +1388,8 @@
   newRef = SpiderHost . newRef
   readRef = SpiderHost . readRef
   writeRef r = SpiderHost . writeRef r
+
+instance MonadAtomicRef SpiderHost where
   atomicModifyRef r = SpiderHost . atomicModifyRef r
 
 instance MonadRef SpiderHostFrame where
@@ -1341,4 +1397,6 @@
   newRef = SpiderHostFrame . newRef
   readRef = SpiderHostFrame . readRef
   writeRef r = SpiderHostFrame . writeRef r
+
+instance MonadAtomicRef SpiderHostFrame where
   atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
