diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -12,9 +12,10 @@
 
 Getting started
 ---------------
-### [Building an App in Eve](https://github.com/ChrisPenner/eve/blob/master/docs/Building-An-App.md)
-
-\^ That guide will bring you through the process of making your first app!
+### [Building A Game in Eve](https://github.com/ChrisPenner/eve/blob/master/examples/tunnel-crawler/README.md)
+[Here's](https://github.com/ChrisPenner/eve/blob/master/examples/tunnel-crawler/README.md) a guide which
+walks you through building your first application in Eve from start to finish, it's quite thorough and it's a great
+place to start!
 
 If you have any issues (and I'm sure there'll be a few; it's a new project!)
 please report them [here](https://github.com/ChrisPenner/eve/issues).
@@ -28,26 +29,140 @@
 - Events
 - State
 
+## Events
 Eve provides many useful combinators for dispatching events and adding
 listeners to events, events are a broad concept in Eve and can be triggered by
 user-interaction, file-changes, even network sockets! Anything you can think of
 really! Each time an event is fired, your app 'reacts' by running any
-associated listeners on the given event. This is quite similar to other event
-systems so far; however Eve does a few things differently. This is where the
-'State' concept comes in. When writing an App, or an extension for an App, in
-Eve, you can specify a state object which you'd like Eve to keep track of for
-you, you can run monadic actions over this state and do whatever you want with
-it. You can even expose your state-changing combinators to other extensions to
-allow them to change the state too! Another nifty thing is that events can be
-dispatched on different levels; so for instance in the Rasa text editor which
-is built using Eve, there's the notion of 'Global Events' and 'Buffer Events'.
-A 'Buffer' is a State object they've defined, and they added the `HasEvents`
-typeclass to it, which now allows them to register listeners and dispatch
-events to a specific instance of a buffer! Trackable states (and therefore
-state-level event listeners) can be nested several levels deep without issue.
-Unlike most event systems, Eve also allows Monoidal return values from event
-listeners, so you can collect 'responses' from each event you fire if you wish.
+associated listeners on the given event. 
 
+The functions you need to know are (with simplified types, see the real type in
+the [hackage docs](https://hackage.haskell.org/package/eve/docs/Eve.html)):
+
+- `dispatchEvent :: forall eventType result m. (Monad m, Monoid result) => eventType -> m result`
+- `addListener :: forall eventType result m. (Monad m, Monoid result) (eventType -> m result) -> m ListenerId`
+
+As I mention above, these types are simplified a bit (and yet they still look
+complicated!). Actually, the types look so complex so that they're simpler to
+use! The `forall` makes it so that you can call `dispatchEvent` with ANY
+Typeable type and it will run the proper event listeners which were registered
+by `addListener`; those listeners can alter app state, or even dispatch more
+events! If the listeners return some (monoidal) value then the results from all
+listeners are combined with `mappend` and are returned. That's pretty much it!
+
+Here's a quick example for those who need to see some code:
+
+```haskell
+import Eve
+import Data.Monoid
+
+-- Define an event to listen for, in this case we don't even need any data alongside it.
+data ComputeScore = ComputeScore
+
+-- Define some computations which calculate some aspect of score.
+-- We accept an argument of 'ComputeScore' to define what this is a listener for
+scoreContributor1, scoreContributor2 :: ComputeScore -> App (Sum Int)
+scoreContributor1 _ = do
+  ... -- do some calculation over app state to determine one aspect of score
+  return (Sum score)
+
+scoreContributor2 _ = do
+  ... -- Calculate some other aspect of the score
+  return (Sum score)
+
+-- In eve's initialization block we register the listeners, we could add these listeners anywhere
+main :: IO ()
+main = eve_ $ do
+  ... -- other initialization (e.g. key listeners, etc.)
+  addListener_ scoreContributor1
+  addListener_ scoreContributor2
+
+  -- This dispatches the triggering event and monoidally sums all the individual score components!
+computeTotalScore :: App (Sum Int)
+computeTotalScore = do
+  Sum score <- dispatchEvent ComputeScore
+  return score
+```
+
+## State
+
+Next we see how Eve handles state. Eve seeks to be as extensible as possible so
+it makes very few assumptions about the type of state that you (or your
+extensions) plan to store. You can define a type of state yourself using `data`
+and then provide actions which alter that state using a `MonadState` instance
+(from mtl). Don't worry if you don't know what that means, here's a real quick
+example which uses the combinators from the [lens
+library](https://hackage.haskell.org/package/lens) to make a few simple state
+changes.
+
+```haskell
+import Eve
+import Control.Lens
+data MyState = MyState
+  { _myInt :: Int
+  , _myString :: String
+  }
+makeLenses ''MyState
+
+-- This alters some state and returns the old string for some reason.
+doSomething :: Action MyState String
+doSomething = do
+  oldString <- use myString
+  myString .= "Hi!"
+  myInt += 1
+  return oldString
+```
+
+So what does this gain us? Well now if we have a `MyState` somewhere in our app we
+can run that Action on it! We can also register that Action as a listener for some
+event!
+
+Now for the interesting part; handling state for extensions. This is usually a bit
+tricky since the types that an extension might use aren't known by you (the app author).
+Eve takes care of this by providing an interface for extensions to store and keep track
+of arbitrary types, while still allowing other extensions to run actions that it exports.
+This is where the `HasStates` typeclass comes in; here's the honest to goodness implementation:
+
+```haskell
+class HasStates s  where
+  states :: Lens' s States
+```
+
+If your state implements that typeclass, then extensions can store their own states inside it!
+It's pretty easy to implement too, let's add it to our `MyState`.
+
+```haskell
+import Eve
+import Control.Lens
+data MyState = MyState
+  { _myInt :: Int
+  , _myString :: String
+  , _myStates :: States
+  }
+makeLenses ''MyState
+
+instance HasStates MyState where
+    states = myStates
+```
+
+Done! We added a new field which has the type `States` which is exported by Eve.
+Then we just took the **lens** created by `makeLenses` and used it in our instance.
+That's it! Now extensions can store their own state inside `Action MyState` by
+using the `stateLens`; check out the [hackage docs](https://hackage.haskell.org/package/eve/docs/Eve.html) 
+on that for more info on how to do it!
+
+Those are the basics, but you can do much more than that if you like!
+Eve also lets you add listeners and dispatch events on an Object specific basis!
+If you have a copy of some state (let's say a single instance of an Enemy in a game)
+you can dispatch events over that enemy individually and any registered (Action Enemy)
+callbacks will be run without affecting any other enemies! Check out `HasEvents`
+to see how that works.
+
+One last cool feature is that event listeners can return information! If your event
+listener results in a return value that's a Monoid (like a list, or string for example)
+you can collect the responses of all the listeners when you call `dispatchEvent`. This
+is a great way for your application to 'ask' extensions about their state.
+
 When designing applications in Eve; it's crucial to think about how the
 state of you application will be stored, and how different components interact.
 Eve works best when components are separated and communicate with each-other through
@@ -58,8 +173,7 @@
 
 ### Pros
 
--   Implementing most core functionality as extensions ensures a powerful and
-    elegant extension interface.
+-   Implementing most core functionality using the event system your app remains extensible.
 -   Flexibility & Adaptability; applications can be written in such a way that
     users can replace entire components with alternate versions.
 
@@ -68,7 +182,6 @@
 -   Module cross-dependencies makes the community infrastructure more fragile,
 -   This architecture takes some getting used-to.
 
-
 Contributing
 ============
 
@@ -92,3 +205,8 @@
 Chatting about features is a key part of Eve's development; come join us in
 the [Chat Room](https://gitter.im/eve-framework/Lobby) to discuss features or 
 improvements!
+
+Related Works
+=============
+
+- [Emulating traditional imperative event loops with functional paradigms](./paper/functional-event-loops.pdf)
diff --git a/eve.cabal b/eve.cabal
--- a/eve.cabal
+++ b/eve.cabal
@@ -1,5 +1,5 @@
 name:                eve
-version:             0.1.2
+version:             0.1.9.0
 synopsis: An extensible event framework
 description: An extensible event-driven application framework in haskell for building embarassingly modular software.
 homepage:            https://github.com/ChrisPenner/eve#readme
@@ -8,7 +8,7 @@
 author:              Chris Penner
 maintainer:          christopher.penner@gmail.com
 copyright:           2017 Chris Penner
-category:            Web
+category:            Framework
 build-type:          Simple
 extra-source-files:  README.md
 cabal-version:       >=1.10
@@ -24,16 +24,14 @@
                      , Eve.Internal.States
                      , Eve.Internal.Listeners
                      , Eve.Internal.Run
-  build-depends:       base >= 4.7 && < 5
+  build-depends:       base >= 4.9 && < 5
                      , mtl
                      , lens
                      , free
-                     , pipes
-                     , pipes-parse
-                     , pipes-concurrency
                      , data-default
                      , containers
   default-language:    Haskell2010
+  ghc-options:         -Wall
 
 test-suite eve-test
   type:                exitcode-stdio-1.0
@@ -43,7 +41,7 @@
                      , EveSpec
                      , Eve.Internal.AsyncSpec
                      , Eve.Internal.ActionsSpec
-                     , Eve.Internal.ExtensionsSpec
+                     , Eve.Internal.StatesSpec
                      , Eve.Internal.EventsSpec
                      , Eve.Internal.ListenersSpec
                      , Eve.Internal.RunSpec
diff --git a/src/Eve.hs b/src/Eve.hs
--- a/src/Eve.hs
+++ b/src/Eve.hs
@@ -1,36 +1,51 @@
 module Eve
   (
-  -- * Running your App
-  eve
+  -- | This documentation is split into parts based on complexity.
+  -- For most applications you'll need only the Simple section. You'll
+  -- find useful tools in the Advanced section once you've got a simple app
+  -- up and running.
 
-  -- * Working with Actions
+  -- * Simple
+  -- | Eve allows you to build your applications incrementally, adding more
+  -- complexity as you need it. For this reason, many of the types are more
+  -- general than you'll likely need. This can be a bit confusing, but here's
+  -- a few tips:
+  --
+  --    * Both 'Action' and 'App' unify with 'ActionT'. You may use them in place of
+  --    'ActionT'. When in doubt, use 'App'.
+  --    * When you see vague references to monads @m@ or @n@, you can use 'App' or 'Action' in its place.
+  --    * Simple Apps assume that you use the provided 'AppState' and it is
+  --      "baked in" to the 'Action' and 'App' types. Wherever you see @'HasStates' s@
+  --      you can mentally replace @s@ with 'AppState'.
+
+  -- ** Running your App
+  eve_
+
+  -- ** Working with Actions
   , App
   , Action
-  , AppT
-  , ActionT
-  , liftApp
+  , runApp
   , runAction
   , exit
 
-  -- * Dispatching Events
+  -- ** Dispatching Events
   , dispatchEvent
   , dispatchEvent_
-  , dispatchEventAsync
-  , dispatchActionAsync
 
-  -- * Event Listeners
+  -- ** Event Listeners
   , addListener
   , addListener_
+
   , removeListener
+
   , Listener
   , ListenerId
 
-  -- * Asynchronous Helpers
-  , asyncActionProvider
+  -- ** Asynchronous Helpers
   , asyncEventProvider
-  , Dispatcher
+  , EventDispatcher
 
-  -- * Built-in Event Listeners
+  -- ** Built-in Event Listeners
   , afterInit
   , beforeEvent
   , beforeEvent_
@@ -38,7 +53,7 @@
   , afterEvent_
   , onExit
 
-  -- * Working with State
+  -- ** Working with State
   -- | All application-provided states are stored in the same
   -- Map; keyed by their 'Data.Typeable.TypeRep'. This means that if more than one state
   -- uses the same type then they'll conflict and overwrite each-other (this is less of a
@@ -48,9 +63,9 @@
   -- @Counter@ newtype when storing it. If you wish to store multiple copies of a given state
   -- simply store them in a list or map, then store that container as your state.
   --
-  -- Because states are stored by their 'Data.Typeable.TypeRep', they must define an
-  -- instance of 'Data.Typeable.Typeable', luckily GHC can derive this for you with
-  -- @deriving Typeable@.
+  -- Because states are stored by their 'Data.Typeable.TypeRep', they must
+  -- define an instance of 'Data.Typeable.Typeable', In most cases it's
+  -- unnecessary, but GHC can derive this for you with @deriving Typeable@.
   --
   -- It is also required for all states to define an instance of
   -- 'Data.Default.Default', this is because accessing an extension which has not
@@ -60,28 +75,58 @@
   -- a default of 'Data.Maybe.Nothing' and pattern-match on its value when you
   -- access it.
   --
-  -- Stored states are accessed by using the `stateLens` lens, this lens is polymorphic
-  -- and can return ANY type. GHC infers the needed type and the lens will retrieve the
-  -- state that you want from the store of states. It seems a bit complicated, but it all
-  -- works fine in practice.
-  --
-  -- To avoid confusion it's best to rename a version of `stateLens` with a more restrictive
-  -- type for each different state type that you store. This helps prevent strange errors and
-  -- makes your code much easier to read. For example:
+  -- Here's an example of defining your own state:
   --
-  -- > data MyState = MyState String
-  -- > myState :: HasStates s => Lens' s MyState
-  -- > myState = stateLens
+  -- > data SimpleState = SimpleState
+  -- >   { _myString :: String
+  -- >   }
+  -- > makeLenses ''SimpleState
   -- >
-  -- > myAction = do
-  -- >   MyState str <- use stateLens
-  --
-  -- If GHC has trouble inferring the type, rename it and restrict the type as above.
+  -- > instance Default SimpleState where
+  -- >   def = SimpleState "default"
+  , makeStateLens
+  , AppState
+
+  -- * Advanced
+  -- | This section provides tools which become relevant when working on more
+  -- complex apps. You can customize which states you operate over, embed events
+  -- in nested states, and choose a custom base monad for the mtl stack.
+
+  , eve
+  -- ** Actions
+  , AppT
+  , ActionT
+  , runActionOver
+
+  -- ** States
   , HasStates(..)
   , States
-  , HasEvents
   , stateLens
-  , AppState
+
+  -- ** Local Events
+
+  -- | The local versions of the event functions are the same as the others ('dispatchEvent',
+  -- 'addListener', 'removeListener') however they operate on a per-state basis.
+  -- This means that if you define a custom state which implements 'HasEvents'
+  -- then you may use these functions inside an `Action CustomState` to dispatch events
+  -- to ONLY the listners within that specific instance of that state. Note that
+  -- these listeners and events are distinct on the value level, not just the type level,
+  -- so if you have multiple copies of CustomState in your app, they each have their
+  -- own disjoint event listeners.
+  , HasEvents
+  , dispatchLocalEvent
+  , dispatchLocalEvent_
+
+  , addLocalListener
+  , addLocalListener_
+
+  , removeLocalListener
+
+  -- ** Async
+  , asyncActionProvider
+
+  , dispatchEventAsync
+  , dispatchActionAsync
   ) where
 
 import Eve.Internal.Run
diff --git a/src/Eve/Internal/Actions.hs b/src/Eve/Internal/Actions.hs
--- a/src/Eve/Internal/Actions.hs
+++ b/src/Eve/Internal/Actions.hs
@@ -1,6 +1,7 @@
 {-# language GeneralizedNewtypeDeriving #-}
 {-# language DeriveFunctor #-}
 {-# language FlexibleInstances #-}
+{-# language FlexibleContexts #-}
 {-# language MultiParamTypeClasses #-}
 {-# language RankNTypes #-}
 {-# language TypeFamilies #-}
@@ -11,24 +12,29 @@
   , ActionT(..)
   , AppT
 
-  , runApp
-  , evalApp
-  , execApp
+  , runEve
+  , evalEve
+  , execEve
 
-  , liftApp
+  , runApp
   , runAction
+  , runActionOver
   ) where
 
+import Eve.Internal.States
 import Control.Monad.State
 import Control.Monad.Trans.Free
 import Control.Lens
+import Data.Typeable
+import Data.Default
+import Data.Semigroup
 
 -- | An 'App' has the same base and zoomed values.
 type AppT s m a = ActionT s s m a
 
 -- | A Free Functor for storing lifted App actions.
 newtype AppF base m next =
-  LiftApp (StateT base m next)
+  RunApp (StateT base m next)
   deriving (Functor, Applicative)
 
 -- | Base Action type. Allows paramaterization over application state, zoomed state
@@ -37,8 +43,18 @@
   { getAction :: FreeT (AppF base m) (StateT zoomed m) a
   } deriving (Functor, Applicative, Monad, MonadIO, MonadState zoomed)
 
+instance (Semigroup a,Monad m) => Semigroup (ActionT base zoomed m a) where
+  a <> b = do
+    a' <- a
+    b' <- b
+    return $ a' <> b'
+
+instance (Monoid a, Monad m) => Monoid (ActionT base zoomed m a) where
+  mempty = return mempty
+  mappend = (<>)
+
 instance Monad n => MonadFree (AppF base n) (ActionT base zoomed n) where
-  wrap (LiftApp act) = join . ActionT . liftF . LiftApp $ act
+  wrap (RunApp act) = join . ActionT . liftF . RunApp $ act
 
 instance MonadTrans (ActionT base zoomed) where
   lift = ActionT . lift . lift
@@ -49,33 +65,39 @@
   step <- runFreeT m
   case step of
     Pure a -> return a
-    Free (LiftApp next) -> next >>= unLift
+    Free (RunApp next) -> next >>= unLift
 
 -- | Allows 'zoom'ing 'Action's.
 type instance Zoomed (ActionT base zoomed m) = Zoomed (FreeT (AppF base m) (StateT zoomed m))
 instance Monad m => Zoom (ActionT base s m) (ActionT base t m) s t where
   zoom l (ActionT action) = ActionT $ zoom l action
 
--- | Given a 'Lens' or 'Traversal' or something similar from "Control.Lens"
+-- | This runs an @'Action' MyState a@ over the MyState which is
+-- stored in the currently focused state and returns the result.
+-- Use 'runActionOver' if you'd like to specify a particular @MyState@
+-- which is accessed by a 'Lens' or 'Traversal'.
+runAction :: (HasStates t, Functor (Zoomed m c), Default s, Typeable s, Zoom m n s t) => m c -> n c
+runAction = zoom stateLens
+
+-- | Given a 'Lens' or 'Traversal' or LensLike from "Control.Lens"
 -- which focuses the state (t) of an 'Action' from a base state (s),
--- this will convert @Action t a -> Action s a@.
---
--- Given a lens @HasStates s => Lens' s t@ it can also convert @Action t a -> App a@
-runAction :: Zoom m n s t => LensLike' (Zoomed m c) t s -> m c -> n c
-runAction = zoom
+-- this will convert @Action t a -> Action s a@ so that it may be run
+-- in an @Action s a@
+runActionOver :: Zoom m n s t => LensLike' (Zoomed m c) t s -> m c -> n c
+runActionOver = zoom
 
--- | Allows you to run an 'App' or 'AppM' inside of an 'Action' or 'ActionM'
-liftApp :: Monad m => AppT base m a -> ActionT base zoomed m a
-liftApp = liftF .  LiftApp . unLift . getAction
+-- | Allows you to run an 'App' inside of an 'Action'
+runApp :: Monad m => AppT base m a -> ActionT base zoomed m a
+runApp = liftF .  RunApp . unLift . getAction
 
 -- | Runs an application and returns the value and state.
-runApp :: Monad m => base -> AppT base m a -> m (a, base)
-runApp baseState = flip runStateT baseState . unLift . getAction
+runEve :: Monad m => base -> AppT base m a -> m (a, base)
+runEve baseState = flip runStateT baseState . unLift . getAction
 
 -- | Runs an application and returns the resulting value.
-evalApp :: Monad m => base -> AppT base m a -> m a
-evalApp baseState = fmap fst . runApp baseState
+evalEve :: Monad m => base -> AppT base m a -> m a
+evalEve baseState = fmap fst . runEve baseState
 
 -- | Runs an application and returns the resulting state.
-execApp :: Monad m => base -> AppT base m a -> m base
-execApp baseState = fmap snd . runApp baseState
+execEve :: Monad m => base -> AppT base m a -> m base
+execEve baseState = fmap snd . runEve baseState
diff --git a/src/Eve/Internal/AppState.hs b/src/Eve/Internal/AppState.hs
--- a/src/Eve/Internal/AppState.hs
+++ b/src/Eve/Internal/AppState.hs
@@ -17,7 +17,7 @@
 import Control.Lens
 import Data.Default
 import Data.Typeable
-import Pipes.Concurrent
+import Control.Concurrent.Chan
 
 -- | A basic default state which underlies 'App' Contains only a map of 'States'.
 data AppState = AppState
@@ -34,23 +34,19 @@
 instance HasEvents AppState where
 
 newtype AsyncQueue base m = AsyncQueue
-  { _asyncQueue' :: Maybe (Output (AppT base m ()))
+  { _asyncQueue' :: Maybe (Chan (AppT base m ()))
   } deriving Typeable
 makeLenses ''AsyncQueue
 
-instance Show (AsyncQueue base m) where
-  show _ = "Async Queue"
-
 instance Default (AsyncQueue base m) where
   def = AsyncQueue Nothing
 
 -- | Accesses a queue for dispatching async actions.
-asyncQueue :: (HasStates s, Typeable m, Typeable base) => Lens' s (Maybe (Output (AppT base m ())))
+asyncQueue :: (HasStates s, Typeable m, Typeable base) => Lens' s (Maybe (Chan (AppT base m ())))
 asyncQueue = stateLens.asyncQueue'
 
 newtype Exiting =
   Exiting Bool
-  deriving (Show, Eq)
 
 instance Default Exiting where
   def = Exiting False
@@ -58,25 +54,26 @@
 -- | Tells the application to quit. This triggers 'onExit' listeners
 -- following the current event loop.
 exit :: (Monad m, HasStates s) => ActionT s zoomed m ()
-exit = liftApp $ stateLens .= Exiting True
+exit = runApp $ stateLens .= Exiting True
 
 -- | Checks whether we're in the process of exiting.
 isExiting :: (Monad m, HasStates s) => ActionT s zoomed m Bool
-isExiting = liftApp $ do
+isExiting = runApp $ do
   Exiting b <- use stateLens
   return b
 
 
--- | An App is a base level monad which operates over your main application
+-- | An 'App' is a base level monad which operates over your main application
 -- state. You may call 'runAction' inside an app to run 'Action's over other states.
--- need to specify your own custom base state.
 type App a = AppT AppState IO a
 
--- | An Action is a monad over some zoomed in state, they are run inside 'App' using
--- 'runAction'. For example an Action which operates over a String somewhere in your app state
+-- | An 'Action' is a monad over some zoomed in state, they are run inside 'App' using
+-- 'runAction'. For example an 'Action' which operates over a String somewhere in your app state
 -- would be written as:
 --
--- alterString :: Action String ()
+-- @
+-- alterString :: 'Action' String ()
+-- @
 type Action state a = ActionT AppState state IO a
 
 -- | A more general version of 'App' which lets you specify the underlying monad.
diff --git a/src/Eve/Internal/Async.hs b/src/Eve/Internal/Async.hs
--- a/src/Eve/Internal/Async.hs
+++ b/src/Eve/Internal/Async.hs
@@ -12,43 +12,49 @@
 import Eve.Internal.AppState
 import Eve.Internal.States
 
+import Control.Concurrent
 import Control.Monad
 import Control.Monad.State
 import Control.Lens
 import Data.Typeable
 
-import Pipes
-import Pipes.Concurrent
-
 -- | Dispatch an action which is generated by some IO. Note that state of the application may have changed
 -- between calling 'dispatchActionAsync' and running the resulting 'Action'
 dispatchActionAsync
   :: (MonadIO m, HasStates base, Typeable m, Typeable base) => IO (AppT base m ()) -> ActionT base zoomed m ()
-dispatchActionAsync asyncAction = liftApp $ do
+dispatchActionAsync asyncAction = runApp $ do
   mQueue <- use asyncQueue
   case mQueue of
     Nothing -> return ()
-    Just queue -> do
-      let effect = (liftIO asyncAction >>= yield) >-> toOutput queue
-      liftIO . void . forkIO $ runEffect effect >> performGC
+    Just queue -> liftIO . void . forkIO $ asyncAction >>= writeChan queue
 
 -- | This allows long-running IO processes to provide 'Action's to the application asyncronously.
+-- 
+-- 'asyncEventProvider' is simpler to use, however 'asyncActionProvider' provides
+-- more power and expressivity. When in doubt, 'asyncEventProvider' probably meets
+-- your needs.
 --
 -- Don't let the type signature confuse you; it's much simpler than it seems.
 --
 -- Let's break it down:
 --
 -- When you call 'asyncActionProvider' you pass it a function which accepts a @dispatch@ function as an argument
--- and then calls it with various 'Action's within the resulting 'IO'.
+-- and then calls it with various 'Action's within the resulting 'IO'. The
+-- @dispatch@ function it is passed will have type @(App () -> IO ())@
 --
 -- Note that this function calls forkIO internally, so there's no need to do that yourself.
+--
+-- Here's an example:
+--
+-- > data Timer = Timer
+-- > myTimer :: (App () -> IO ()) -> IO ()
+-- > myTimer dispatch = forever $ dispatch (myInt += 1) >> threadDelay 1000000
+-- >
+-- > myInit :: App ()
+-- > myInit = asyncActionProvider myTimer
 asyncActionProvider :: (MonadIO m, HasStates base, Typeable m, Typeable base) => ((AppT base m () -> IO ()) -> IO ()) -> ActionT base zoomed m ()
-asyncActionProvider provider = liftApp $ do
+asyncActionProvider provider = runApp $ do
   mQueue <- use asyncQueue
   case mQueue of
     Nothing -> return ()
-    Just queue -> do
-      let dispatcher action =
-            let effect = yield action >-> toOutput queue
-            in void . forkIO $ runEffect effect >> performGC
-      liftIO . void . forkIO $ provider dispatcher
+    Just queue -> liftIO . void . forkIO $ provider (writeChan queue)  where
diff --git a/src/Eve/Internal/Listeners.hs b/src/Eve/Internal/Listeners.hs
--- a/src/Eve/Internal/Listeners.hs
+++ b/src/Eve/Internal/Listeners.hs
@@ -7,11 +7,18 @@
   ( HasEvents
   , dispatchEvent
   , dispatchEvent_
+  , dispatchLocalEvent
+  , dispatchLocalEvent_
   , dispatchEventAsync
 
   , addListener
   , addListener_
+  , addLocalListener
+  , addLocalListener_
+
   , removeListener
+  , removeLocalListener
+
   , asyncEventProvider
 
   , afterInit
@@ -23,7 +30,7 @@
 
   , Listener
   , ListenerId
-  , Dispatcher
+  , EventDispatcher
   ) where
 
 import Eve.Internal.States
@@ -42,29 +49,55 @@
 -- | Registers an action to be performed directly following the Initialization phase.
 --
 -- At this point any listeners in the initialization block have run, so you may 'dispatchEvent's here.
-afterInit :: forall base zoomed m a. (Monad m, HasEvents zoomed, Typeable m, Typeable base) => ActionT base zoomed m a -> ActionT base zoomed m ()
-afterInit action = void $ addListener (const (void action) :: AfterInit -> ActionT base zoomed m ())
+afterInit :: forall base m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> AppT base m ()
+afterInit action = addListener_ (const (void action) :: AfterInit -> AppT base m ())
 
 -- | Registers an action to be performed BEFORE each async event is processed phase.
-beforeEvent :: forall base zoomed m a. (Monad m, HasEvents zoomed, Typeable m, Typeable base) => ActionT base zoomed m a -> ActionT base zoomed m ListenerId
-beforeEvent action = addListener (const (void action) :: BeforeEvent -> ActionT base zoomed m ())
+beforeEvent :: forall base zoomed m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ListenerId
+beforeEvent action = addListener (const (void action) :: BeforeEvent -> AppT base m ())
 
-beforeEvent_ :: (Monad m, HasEvents zoomed, Typeable m, Typeable base) => ActionT base zoomed m a -> ActionT base zoomed m ()
+beforeEvent_ :: (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ()
 beforeEvent_ = void . beforeEvent
 
 -- | Registers an action to be performed AFTER each event phase.
-afterEvent :: forall base zoomed m a. (Monad m, HasEvents zoomed, Typeable m, Typeable base) => ActionT base zoomed m a -> ActionT base zoomed m ListenerId
-afterEvent action = addListener (const (void action) :: AfterEvent -> ActionT base zoomed m ())
+afterEvent :: forall base zoomed m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ListenerId
+afterEvent action = addListener (const (void action) :: AfterEvent -> AppT base m ())
 
-afterEvent_ :: (Monad m, HasEvents zoomed, Typeable m, Typeable base) => ActionT base zoomed m a -> ActionT base zoomed m ()
+afterEvent_ :: (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ()
 afterEvent_ = void . afterEvent
 
 -- | Registers an action to be run before shutdown. Any asynchronous combinators used in this block will NOT be run.
-onExit :: forall base zoomed m a. (HasEvents zoomed, Typeable m, Typeable base, Monad m) => ActionT base zoomed m a -> ActionT base zoomed m ()
-onExit action = void $ addListener (const $ void action :: Exit -> ActionT base zoomed m ())
+onExit :: forall base zoomed m a. (HasEvents base, Typeable m, Monad m) => AppT base m a -> ActionT base zoomed m ()
+onExit action = addListener_ (const (void action) :: Exit -> AppT base m ())
 
--- | Given an Event of any type, this runs any listeners registered for that event type with the provided event.
--- Events may also contain data pertaining to the event and it will be passed to the listeners.
+-- | A local version of 'dispatchEvent'.
+-- The local version dispatches the event in the context of the current 'Action',
+-- If you don't know what this means, you probably want 'dispatchEvent' instead
+dispatchLocalEvent
+  :: forall result eventType m s.
+     (MonadState s m
+     ,HasEvents s
+     ,Monoid result
+     ,Typeable m
+     ,Typeable eventType
+     ,Typeable result)
+  => eventType -> m result
+dispatchLocalEvent evt = do
+  LocalListeners _ listeners <- use localListeners
+  results <-
+    traverse ($ evt) (matchingListeners listeners :: [eventType -> m result])
+  return (mconcat results :: result)
+
+dispatchLocalEvent_
+  :: forall eventType m s.
+     (MonadState s m
+     ,HasEvents s
+     ,Typeable m
+     ,Typeable eventType)
+  => eventType -> m ()
+dispatchLocalEvent_ = dispatchLocalEvent
+
+-- | Runs any listeners registered for the provided event with the provided event;
 --
 -- You can also 'query' listeners and receive a ('Monoid'al) result.
 --
@@ -89,37 +122,28 @@
 -- >   liftIO $ print lastNames
 -- >   -- ["Smith", "Jenkins"]
 dispatchEvent
-  :: forall result eventType m s.
-     (MonadState s m
-     ,HasEvents s
+  :: forall result eventType m base zoomed.
+     (HasEvents base
      ,Monoid result
+     ,Monad m
      ,Typeable m
      ,Typeable eventType
      ,Typeable result)
-  => eventType -> m result
-dispatchEvent evt = do
-  LocalListeners _ listeners <- use localListeners
-  results <-
-    traverse ($ evt) (matchingListeners listeners :: [eventType -> m result])
-  return (mconcat results :: result)
+    => eventType -> ActionT base zoomed m result
+dispatchEvent evt = runApp $ dispatchLocalEvent evt
 
 dispatchEvent_
-  :: forall eventType m s.
-     (MonadState s m
-     ,HasEvents s
+  :: forall eventType m base zoomed.
+     (HasEvents base
+     ,Monad m
      ,Typeable m
      ,Typeable eventType)
-  => eventType -> m ()
+    => eventType -> ActionT base zoomed m ()
 dispatchEvent_ = dispatchEvent
 
--- | Registers an 'Action' or 'App' to respond to an event.
---
--- For a given use: @addListener myListener@, @myListener@ might have the type @MyEvent -> App a@
--- it will register the function @myListener@ to be run in response to a @dispatchEvent (MyEvent eventInfo)@
--- and will be provided @(MyEvent eventInfo)@ as an argument.
---
--- This returns a 'ListenerId' which corresponds to the registered listener for use with 'removeListener'
-addListener
+-- | The local version of 'addListener'. It will register a listener within an 'Action's local event
+-- context. If you don't know what this means you probably want 'addListener' instead.
+addLocalListener
   :: forall result eventType m s.
      (MonadState s m
      ,HasEvents s
@@ -128,7 +152,7 @@
      ,Typeable result
      ,Monoid result)
   => (eventType -> m result) -> m ListenerId
-addListener lFunc = do
+addLocalListener lFunc = do
   LocalListeners nextListenerId listeners <- use localListeners
   let (listener, listenerId, eventType) = mkListener nextListenerId lFunc
       newListeners = M.insertWith mappend eventType [listener] listeners
@@ -145,7 +169,7 @@
           prox = typeRep (Proxy :: Proxy event)
       in (list, listId, prox)
 
-addListener_
+addLocalListener_
   :: forall result eventType m s.
      (MonadState s m
      ,HasEvents s
@@ -154,13 +178,44 @@
      ,Typeable result
      ,Monoid result)
   => (eventType -> m result) -> m ()
+addLocalListener_ = void . addLocalListener
+
+-- | Registers an 'Action' or 'App' to respond to an event.
+--
+-- For a given use: @addListener myListener@, @myListener@ might have the type @MyEvent -> App a@
+-- it will register the function @myListener@ to be run in response to a @dispatchEvent (MyEvent eventInfo)@
+-- and will be provided @(MyEvent eventInfo)@ as an argument.
+--
+-- This returns a 'ListenerId' which corresponds to the registered listener for use with 'removeListener'
+addListener
+  :: forall result eventType m base zoomed.
+     (HasEvents base
+     ,Monad m
+     ,Typeable m
+     ,Typeable eventType
+     ,Typeable result
+     ,Monoid result)
+    => (eventType -> AppT base m result) -> ActionT base zoomed m ListenerId
+addListener = runApp . addLocalListener
+
+addListener_
+  :: forall result eventType m base zoomed.
+     (HasEvents base
+     ,Monad m
+     ,Typeable m
+     ,Typeable eventType
+     ,Typeable result
+     ,Monoid result)
+    => (eventType -> AppT base m result) -> ActionT base zoomed m ()
 addListener_ = void . addListener
 
--- | Unregisters a listener referred to by the provided 'ListenerId'
-removeListener
+-- | The local version of 'removeListener'.
+-- This removes a listener from an 'Action's event context. If you don't
+-- know what this means you probably want 'removeListener' instead.
+removeLocalListener
   :: (MonadState s m, HasEvents s)
   => ListenerId -> m ()
-removeListener listenerId@(ListenerId _ eventType) = localListeners %= remover
+removeLocalListener listenerId@(ListenerId _ eventType) = localListeners %= remover
   where
     remover (LocalListeners nextListenerId listeners) =
       let newListeners =
@@ -168,6 +223,13 @@
       in LocalListeners nextListenerId newListeners
     notMatch idA (Listener _ idB _) = idA /= idB
 
+-- | Unregisters a listener referred to by the provided 'ListenerId'
+removeListener
+  :: (HasEvents base
+     ,Monad m)
+    => ListenerId -> ActionT base zoomed m ()
+removeListener = runApp . removeLocalListener
+
 -- | This function takes an IO which results in some event, it runs the IO
 -- asynchronously, THEN dispatches the event. Note that only the
 -- code which generates the event is asynchronous, not any responses to the event
@@ -187,16 +249,11 @@
              Monoid result, HasStates s) =>
             TypeRep -> ListenerId -> (eventType -> m result) -> Listener
 
-instance Show Listener where
-  show (Listener rep (ListenerId n _) _) =
-    "<Listener #" ++ show n ++ ", " ++ show rep ++ ">"
-
 -- | An opaque reverence to a specific registered event-listener.
 -- A ListenerId is used only to remove listeners later with 'removeListener'.
 data ListenerId =
   ListenerId Int
              TypeRep
-  deriving (Show)
 
 instance Eq ListenerId where
   ListenerId a _ == ListenerId b _ = a == b
@@ -208,7 +265,6 @@
 data LocalListeners =
   LocalListeners Int
                  Listeners
-  deriving (Show)
 
 instance Default LocalListeners where
   def = LocalListeners 0 M.empty
@@ -238,7 +294,7 @@
 -- | This is a type alias to make defining your functions for use with 'asyncEventProvider' easier;
 -- It represents the function your event provider function will be passed to allow dispatching
 -- events. Using this type requires the @RankNTypes@ language pragma.
-type Dispatcher = forall event. Typeable event =>
+type EventDispatcher = forall event. Typeable event =>
                                 event -> IO ()
 
 -- | This allows long-running IO processes to provide Events to the application asyncronously.
@@ -247,9 +303,9 @@
 --
 -- Let's break it down:
 --
--- Using the 'Dispatcher' type with asyncEventProvider requires the @RankNTypes@ language pragma.
+-- Using the 'EventDispatcher' type with asyncEventProvider requires the @RankNTypes@ language pragma.
 --
--- This type as a whole represents a function which accepts a 'Dispatcher' and returns an 'IO';
+-- This type as a whole represents a function which accepts an 'EventDispatcher' and returns an 'IO';
 -- the dispatcher itself accepts data of ANY 'Typeable' type and emits it as an event.
 --
 -- When you call 'asyncEventProvider' you pass it a function which accepts a @dispatch@ function as an argument
@@ -261,15 +317,15 @@
 --
 -- > {-# language RankNTypes #-}
 -- > data Timer = Timer
--- > myTimer :: Dispatcher -> IO ()
+-- > myTimer :: EventDispatcher -> IO ()
 -- > myTimer dispatch = forever $ dispatch Timer >> threadDelay 1000000
 -- >
 -- > myInit :: App ()
 -- > myInit = asyncEventProvider myTimer
 asyncEventProvider
-  :: (HasEvents base, MonadIO m, Typeable m) => (Dispatcher -> IO ()) -> ActionT base zoomed m ()
+  :: (HasEvents base, MonadIO m, Typeable m) => (EventDispatcher -> IO ()) -> ActionT base zoomed m ()
 asyncEventProvider asyncEventProv = asyncActionProvider $ eventsToActions asyncEventProv
   where
-    eventsToActions :: (Monad m, HasEvents base, Typeable m) => (Dispatcher -> IO ()) -> (AppT base m () -> IO ()) -> IO ()
+    eventsToActions :: (Monad m, HasEvents base, Typeable m) => (EventDispatcher -> IO ()) -> (AppT base m () -> IO ()) -> IO ()
     eventsToActions aEventProv dispatcher =
       aEventProv (dispatcher . dispatchEvent)
diff --git a/src/Eve/Internal/Run.hs b/src/Eve/Internal/Run.hs
--- a/src/Eve/Internal/Run.hs
+++ b/src/Eve/Internal/Run.hs
@@ -9,16 +9,26 @@
 import Eve.Internal.States()
 import Eve.Internal.AppState
 
+import Control.Concurrent.Chan
 import Control.Monad
 import Control.Monad.State
 import Control.Lens
 import Data.Default
-import Data.Maybe
 import Data.Typeable
 
-import Pipes
-import Pipes.Concurrent
-import qualified Pipes.Parse as PP
+-- | This runs your application like 'eve_',
+-- It is polymorphic in the Monad it operates over, so you may use it with any
+-- custom base monad which implements 'MonadIO'. Upon termination of the app it
+-- returns the final 'AppState'.
+eve :: (MonadIO m, Typeable m) => AppT AppState m () -> m AppState
+eve initialize = do
+  chan <- liftIO newChan
+  execEve (def & asyncQueue .~ Just chan) $ do
+    initialize
+    dispatchEvent_ Init
+    dispatchEvent_ AfterInit
+    eventLoop chan
+    dispatchEvent_ Exit
 
 -- | This runs your application. It accepts an initialization block (which
 -- is the same as any other 'App' or 'Action' block, which
@@ -26,44 +36,27 @@
 -- block should use 'dispatchEvent' since it is possible that not all listeners
 -- have yet been registered. You can use the 'afterInit' trigger to dispatch
 -- any events you'd like to run at start-up.
---
--- It is polymorphic in the Monad it operates over, so you may use it with any 
--- custom base monad which implements 'MonadIO'.
---
--- If you don't need this functionality; the easiest way to get started is to simply
--- cally it like so:
+-- Here's a simple example:
 --
 -- > import Eve
 -- >
 -- > initialize = App ()
 -- > initialize = do
--- >   addListener ...
--- >   ...
+-- >   addListener_ myListener
+-- >   asyncEventProvider myProvider
 -- >
 -- > startApp :: IO ()
 -- > startApp = eve_ initialize
-eve :: (MonadIO m, Typeable m) => AppT AppState m () -> m AppState
-eve initialize = do
-  (output, input) <- liftIO $ spawn unbounded
-  execApp (def & asyncQueue .~ Just output) $ do
-    initialize
-    dispatchEvent_ Init
-    dispatchEvent_ AfterInit
-    eventLoop $ fromInput input
-    dispatchEvent_ Exit
-
--- | 'eve' with '()' as its return value.
-eve_ :: (MonadIO m, Typeable m) => AppT AppState m () -> m ()
+eve_ :: App () -> IO ()
 eve_ = void . eve
 
 -- | This is the main event loop, it runs recursively forever until something
 -- sets the exit status. It runs the pre-event listeners, then checks if any
 -- async events have finished, then runs the post event listeners and repeats.
-eventLoop :: (MonadIO m, HasEvents base, Typeable m) => Producer (AppT base m ()) IO () -> AppT base m ()
-eventLoop producer = do
+eventLoop :: (MonadIO m, HasEvents base, Typeable m) => Chan (AppT base m ()) -> AppT base m ()
+eventLoop chan = do
   dispatchEvent_ BeforeEvent
-  (mAction, nextProducer) <- liftIO $ PP.runStateT PP.draw producer
-  fromMaybe (return ()) mAction
+  join . liftIO $ readChan chan
   dispatchEvent_ AfterEvent
   shouldExit <- isExiting
-  unless shouldExit $ eventLoop nextProducer
+  unless shouldExit $ eventLoop chan
diff --git a/src/Eve/Internal/States.hs b/src/Eve/Internal/States.hs
--- a/src/Eve/Internal/States.hs
+++ b/src/Eve/Internal/States.hs
@@ -1,11 +1,13 @@
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# language RankNTypes #-}
 
 module Eve.Internal.States
   ( States
   , HasStates(..)
   , HasEvents
   , stateLens
+  , makeStateLens
   ) where
 
 import Control.Lens
@@ -17,12 +19,9 @@
 
 -- | A wrapper to allow storing types of states in the same place.
 data StateWrapper =
-  forall s. (Typeable s, Show s) =>
+  forall s. (Typeable s) =>
               StateWrapper s
 
-instance Show StateWrapper where
-  show (StateWrapper s) = show s
-
 -- | A map of state types to their current value.
 type States = Map TypeRep StateWrapper
 
@@ -38,7 +37,7 @@
 -- empty instance:
 --
 -- > instance HasEvents MyState where
--- -- Don't need anything here.
+-- > -- Don't need anything here.
 class (Typeable s, HasStates s) =>
       HasEvents s
 
@@ -46,7 +45,7 @@
 -- It returns the default value ('def') if a state has not yet been set.
 stateLens
   :: forall a e.
-    (Show a, Typeable a, Default a, HasStates e)
+    (Typeable a, Default a, HasStates e)
   => Lens' e a
 stateLens = lens getter setter
   where
@@ -55,3 +54,35 @@
     setter s new =
       set (states . at (typeRep (Proxy :: Proxy a)) . mapping coerce) (Just new) s
     coerce = iso (\(StateWrapper x) -> unsafeCoerce x) StateWrapper
+
+-- | A utility which creates a state-nested version of a lens.
+-- If you pass this function a lens from your state to one of its fields,
+-- it will return a lens which can be used within an 'App' or 'Action'.
+--
+-- The resulting lens will be of type: @newLens :: HasStates s => Lens' s MyState@
+-- Or if you prefer, you may wish to specify the state it operates over more specifically
+-- to prevent using the lens where it was not originally planned. For instance:
+-- @newLens :: Lens' AppState MyState@
+--
+-- > data SimpleState = SimpleState
+-- >   { _myString :: String
+-- >   }
+-- > makeLenses ''SimpleState
+-- >
+-- > instance Default SimpleState where
+-- >   def = SimpleState "default"
+-- >
+-- > myStringStateLens :: HasStates s => Lens' s String
+-- > myStringStateLens = makeStateLens myString
+-- >
+-- > myAction :: App ()
+-- > myAction = do
+-- >   myStringStateLens .= "Hi!"
+-- >   str <- use myStringStateLens
+-- >   liftIO $ print str
+-- > -- "Hi!"
+--
+-- For more complex Prisms or Traversals you can write your own using
+-- 'stateLens'
+makeStateLens :: (HasStates s, Typeable myState, Default myState) => Lens' myState a -> Lens' s a
+makeStateLens = (stateLens .)
diff --git a/src/Eve/Testing.hs b/src/Eve/Testing.hs
--- a/src/Eve/Testing.hs
+++ b/src/Eve/Testing.hs
@@ -9,4 +9,4 @@
 import Data.Default
 
 noIOTest :: AppT AppState Identity a -> (a, AppState)
-noIOTest = runIdentity . runApp def
+noIOTest = runIdentity . runEve def
diff --git a/test/Eve/Internal/ActionsSpec.hs b/test/Eve/Internal/ActionsSpec.hs
--- a/test/Eve/Internal/ActionsSpec.hs
+++ b/test/Eve/Internal/ActionsSpec.hs
@@ -1,3 +1,4 @@
+{-# language TemplateHaskell #-}
 module Eve.Internal.ActionsSpec where
 
 import Test.Hspec
@@ -8,16 +9,40 @@
 
 import Control.Lens
 import Control.Monad.State
+import Data.Default
 
 appendEx  :: Monad m => ActionT AppState String m ()
 appendEx  = modify (++ "!!")
 
-liftAppTest :: Monad m => ActionT AppState String m String
-liftAppTest = do
+runAppTest :: Monad m => ActionT AppState String m String
+runAppTest = do
   put "new"
-  liftApp $ runAction stateLens appendEx
+  runApp $ runAction appendEx
   get
 
+data SimpleState = SimpleState
+  { _myInt :: Int
+  , _myString :: String
+  }
+makeLenses ''SimpleState
+
+instance Default SimpleState where
+  def = SimpleState 0 "default"
+
+alterSimpleState :: ActionT AppState SimpleState Identity String
+alterSimpleState = do
+  myString .= "Hello!"
+  use myString
+
+monoidTest :: ActionT AppState SimpleState Identity (String, [Int])
+monoidTest = do
+  result <- one `mappend` two
+  str <- use myString
+  return (str, result)
+  where
+    one = myString .= "first" >> return [1]
+    two = myString <>= "second" >> return [2]
+
 spec :: Spec
 spec = do
   describe "Exiting" $ do
@@ -25,17 +50,31 @@
       let (didExit, _) = noIOTest (exit >> isExiting)
        in didExit `shouldBe` True
 
-  describe "runAction " $ do
+  describe "runAction" $ do
     it "runs lifted actions to zoomed monad" $
-      let (runActionResult, _) = noIOTest $ runAction stateLens (put "new" >> appendEx) >> runAction stateLens get
+      let (runActionResult, _) = noIOTest $ runAction (put "new" >> appendEx) >> runAction get
        in runActionResult `shouldBe` "new!!"
 
+  describe "runActionOver" $ do
+    it "runs lifted actions to zoomed monad" $
+      let (runActionOverResult, _) = noIOTest $ runActionOver stateLens (put "new" >> appendEx) >> runActionOver stateLens get
+       in runActionOverResult `shouldBe` "new!!"
+
     it "runs over traversals" $
-      let (traversalResult, _) = noIOTest $ runAction stateLens (put $ Just "new") >> runAction (stateLens._Just) (appendEx >> get)
+      let (traversalResult, _) = noIOTest $ runActionOver stateLens (put $ Just "new") >> runActionOver (stateLens._Just) (appendEx >> get)
        in traversalResult `shouldBe` "new!!"
 
-  describe "liftApp" $ do
+  describe "runApp" $ do
     it "runs lifted actions to zoomed monad" $
-      let (liftAppResult, _) = noIOTest (runAction stateLens liftAppTest :: AppT AppState Identity String)
-       in liftAppResult `shouldBe` "new!!"
+      let (runAppResult, _) = noIOTest (runAction runAppTest :: AppT AppState Identity String)
+       in runAppResult `shouldBe` "new!!"
 
+  describe "Can run actions over non-HasStates states" $ do
+    it "compiles" $
+      let (alterationResult, _) = noIOTest (runAction alterSimpleState :: AppT AppState Identity String)
+       in alterationResult `shouldBe` "Hello!"
+
+  describe "is a Monoid" $ do
+    it "combines results and effects" $
+      let (monoidResult, _) = noIOTest (runAction monoidTest :: AppT AppState Identity (String, [Int]))
+       in monoidResult `shouldBe` ("firstsecond", [1, 2])
diff --git a/test/Eve/Internal/ExtensionsSpec.hs b/test/Eve/Internal/ExtensionsSpec.hs
deleted file mode 100644
--- a/test/Eve/Internal/ExtensionsSpec.hs
+++ /dev/null
@@ -1,6 +0,0 @@
-module Eve.Internal.ExtensionsSpec where
-
-import Test.Hspec
-
-spec :: Spec
-spec = return ()
diff --git a/test/Eve/Internal/ListenersSpec.hs b/test/Eve/Internal/ListenersSpec.hs
--- a/test/Eve/Internal/ListenersSpec.hs
+++ b/test/Eve/Internal/ListenersSpec.hs
@@ -34,22 +34,53 @@
   asyncEventProvider (\d -> d CustomEvent)
   store .= "new"
 
+localStateIsolationTest :: ActionT AppState NestedStates Identity String
+localStateIsolationTest = do
+  addLocalListener (const (nestedString .= "new") :: CustomEvent -> ActionT AppState NestedStates Identity ())
+  dispatchEvent_ CustomEvent
+  use nestedString
+
+globalStateIsolationTest :: AppT AppState Identity String
+globalStateIsolationTest = do
+  addListener (const (store .= "new") :: CustomEvent -> AppT AppState Identity ())
+  runActionOver nestedStates $ do
+    dispatchLocalEvent_ CustomEvent
+  use store
+
+listenerPassThroughTest :: AppT AppState Identity String
+listenerPassThroughTest = do
+  let nestedAction :: ActionT AppState NestedStates Identity ()
+      nestedAction = do
+        addListener (const (store .= "new") :: CustomEvent -> AppT AppState Identity ())
+        dispatchEvent CustomEvent
+  runActionOver nestedStates nestedAction
+  use store
+
+
 data OtherEvent = OtherEvent
 multiAsyncEventsTest :: App ()
 multiAsyncEventsTest = do
   addListener (const exit :: CustomEvent -> App ())
   addListener (const (store .= "new") :: OtherEvent -> App ())
-  asyncEventProvider (\d -> d CustomEvent >> d OtherEvent)
+  asyncEventProvider (\d -> d OtherEvent >> d CustomEvent)
 
 spec :: Spec
 spec = do
   describe "dispatchEvent/addListener" $
     it "Triggers Listeners" $ fst (noIOTest basicAction) `shouldBe` "new"
+
+  describe "local events" $ do
+    it "local state is isolated from global events" $ fst (noIOTest $ runAction localStateIsolationTest) `shouldBe` "default"
+    it "global state is isolated from local events" $ fst (noIOTest globalStateIsolationTest) `shouldBe` "default"
+    it "global event actions pass through from local state" $ fst (noIOTest listenerPassThroughTest) `shouldBe` "new"
+
   describe "dispatchEventAsync" $ do
     delayedExitState <- ioTest delayedExit
     it "Triggers Listeners Eventually" $ (delayedExitState ^. store) `shouldBe` "new"
+
   describe "removeListener" $
     it "Removes Listeners" $ fst (noIOTest removeListenersTest) `shouldBe` "default"
+
   describe "asyncEventProvider" $ do
     asyncEventsResult <- ioTest asyncEventsTest
     it "Provides events eventually" $ (asyncEventsResult ^. store) `shouldBe` "new"
diff --git a/test/Eve/Internal/StatesSpec.hs b/test/Eve/Internal/StatesSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Eve/Internal/StatesSpec.hs
@@ -0,0 +1,32 @@
+{-# language TemplateHaskell #-}
+module Eve.Internal.StatesSpec where
+
+import Test.Hspec
+import Fixtures
+import Eve
+
+import Control.Lens
+import Data.Default
+
+data SimpleState = SimpleState
+  { _myString :: String
+  }
+makeLenses ''SimpleState
+
+instance Default SimpleState where
+  def = SimpleState "default"
+
+myStringStateLens :: HasStates s => Lens' s String
+myStringStateLens = makeStateLens myString
+
+makeStateLensTest :: AppT AppState Identity String
+makeStateLensTest = do
+  myStringStateLens .= "new"
+  use (stateLens.myString)
+
+spec :: Spec
+spec = do
+  describe "makeStateLens" $ do
+    it "creates a lens which accesses state from States" $
+      let (makeStateLensResult, _) = noIOTest makeStateLensTest
+       in makeStateLensResult `shouldBe` "new"
diff --git a/test/Fixtures.hs b/test/Fixtures.hs
--- a/test/Fixtures.hs
+++ b/test/Fixtures.hs
@@ -4,6 +4,9 @@
   , CustomEvent(..)
   , noIOTest
   , ioTest
+  , NestedStates
+  , nestedString
+  , nestedStates
   ) where
 
 import Eve.Testing
@@ -19,9 +22,26 @@
 import Test.Hspec.Core.Spec (SpecM)
 import Test.Hspec
 
+data NestedStates = NestedStates
+  { _nestedStates' :: States
+  , _nestedString :: String
+  }
+makeLenses ''NestedStates
+
+instance Default NestedStates where
+  def = NestedStates mempty "default"
+
+instance HasStates NestedStates where
+  states = nestedStates'
+
+instance HasEvents NestedStates
+
+nestedStates :: HasStates s => Lens' s NestedStates
+nestedStates = stateLens
+
 data Store = Store
   {_payload :: String
-  } deriving (Show, Eq)
+  }
 makeLenses ''Store
 
 store :: HasStates s => Lens' s String
