diff --git a/box.cabal b/box.cabal
--- a/box.cabal
+++ b/box.cabal
@@ -1,8 +1,8 @@
 cabal-version:      2.4
 name:               box
-version:            0.7.0
+version:            0.8.0
 synopsis:           boxes
-description:        concurrent, effectful boxes
+description:        A profunctor effect
 category:           project
 homepage:           https://github.com/tonyday567/box#readme
 bug-reports:        https://github.com/tonyday567/box/issues
@@ -12,7 +12,7 @@
 license:            BSD-3-Clause
 license-file:       LICENSE
 build-type:         Simple
-tested-with:        GHC ==8.8.4 || ==8.10.4 || ==9.0.1 || ==9.2.0.20210821
+tested-with:        GHC ==8.8.4 || ==8.10.7 || ==9.2.1
 extra-source-files: ChangeLog.md
 
 source-repository head
@@ -23,10 +23,11 @@
   exposed-modules:
     Box
     Box.Box
+    Box.Codensity
     Box.Committer
     Box.Connectors
-    Box.Cont
     Box.Emitter
+    Box.Functor
     Box.IO
     Box.Queue
     Box.Time
@@ -40,18 +41,18 @@
     -Wunused-packages
 
   build-depends:
-    , attoparsec     ^>=0.14
     , base           >=4.7   && <5
     , concurrency    ^>=1.11
     , containers     ^>=0.6
     , contravariant  ^>=1.5
     , exceptions     ^>=0.10
-    , lens           ^>=5.0
-    , mmorph         ^>=1.2
     , mtl            ^>=2.2.2
     , profunctors    ^>=5.6
     , text           ^>=1.2
     , time           ^>=1.9
     , transformers   ^>=0.5
+    , kan-extensions ^>=5.2
+    , semigroupoids  ^>=5.3
+    , dlist          ^>=1.0
 
   default-language:   Haskell2010
diff --git a/src/Box.hs b/src/Box.hs
--- a/src/Box.hs
+++ b/src/Box.hs
@@ -8,24 +8,17 @@
 {-# LANGUAGE TypeFamilies #-}
 {-# OPTIONS_GHC -Wall #-}
 
--- | Effectful, profunctor boxes designed for concurrency.
---
--- This library follows the ideas and code from [pipes-concurrency](https://hackage.haskell.org/package/pipes-concurrency) and [mvc](https://hackage.haskell.org/package/mvc) but with some polymorphic tweaks and definitively more pretentious names.
+-- | A profunctor effect
 --
 -- “Boxes are surprisingly bulky. Discard or recycle the box your cell phone comes in as soon as you unpack it. You don’t need the manual or the CD that comes with it either. You’ll figure out the applications you need through using it.” — Marie Kondo
 module Box
   ( -- $usage
-    -- $continuations
-    -- $boxes
-    -- $commit
-    -- $emit
-    -- $state
-    -- $finite
     module Box.Box,
     module Box.Committer,
     module Box.Connectors,
-    module Box.Cont,
+    module Box.Codensity,
     module Box.Emitter,
+    module Box.Functor,
     module Box.IO,
     module Box.Queue,
     module Box.Time,
@@ -35,212 +28,33 @@
 import Box.Box
 import Box.Committer
 import Box.Connectors
-import Box.Cont
+import Box.Codensity
 import Box.Emitter
 import Box.IO
 import Box.Queue
 import Box.Time
+import Box.Functor
 
 -- $setup
 -- >>> :set -XOverloadedStrings
--- >>> :set -XGADTs
--- >>> :set -XFlexibleContexts
--- >>> import Data.Functor.Contravariant
+-- >>> import Prelude
 -- >>> import Box
--- >>> import Control.Applicative
--- >>> import Control.Monad.Conc.Class as C
--- >>> import Control.Lens
--- >>> import qualified Data.Sequence as Seq
--- >>> import Data.Text (pack, Text)
--- >>> import Data.Functor.Contravariant
--- >>> import Data.Foldable
--- >>> import Data.Bool
--- >>> import Control.Monad.Morph
--- >>> import Control.Monad.State.Lazy
+-- >>> pushList <$|> qList [1,2,3]
+-- [1,2,3]
+--
+-- >>> glue toStdout <$|> qList ["a", "b", "c"]
+-- a
+-- b
+-- c
 
 -- $usage
 -- >>> :set -XOverloadedStrings
--- >>> :set -XGADTs
--- >>> :set -XFlexibleContexts
--- >>> import Data.Functor.Contravariant
 -- >>> import Box
--- >>> import Control.Monad.Conc.Class as C
--- >>> import Control.Lens
--- >>> import qualified Data.Sequence as Seq
--- >>> import Data.Text (pack, Text)
-
--- $continuations
---
--- Continuations are very common in the API with 'Cont' as an inhouse type.
---
--- > :t fromListE [1..3::Int]
--- fromListE [1..3::Int] :: MonadConc m => Cont m (Emitter m Int)
---
--- The applicative is usually the easiest way to think about and combine continuations with their unadorned counterparts.
---
--- >>> let box' = Box <$> pure toStdout <*> fromListE (pack <$> ["a", "b"])
--- >>> :t box'
--- box' :: Cont IO (Box IO Text Text)
-
--- $boxes
---
--- The two basic ways of connecting up a box are related as follows:
---
--- > glue c e == glueb (Box c e)
--- > glueb == fuse (pure . pure)
---
--- >>> fromToList_ [1..3] glueb
--- [1,2,3]
---
--- >>> fromToList_ [1..3] (fuse (pure . pure))
--- [1,2,3]
---
--- 1. glue: direct fusion of committer and emitter
---
--- >>> runCont $ glue <$> pure toStdout <*> fromListE ((pack . show) <$> [1..3])
--- 1
--- 2
--- 3
---
--- Variations to the above code include:
---
--- Use of continuation applicative operators:
---
--- - the '(<*.>)' operator is short hand for runCont $ xyz 'Control.Applicative.(<*>)' zy.
---
--- - the '(<$.>)' operator is short hand for runCont $ xyz 'Control.Applicative.(<$>)' zy.
---
--- > glue <$> pure toStdout <*.> fromListE ((pack . show) <$> [1..3])
--- > glue toStdout <$.> fromListE ((pack . show) <$> [1..3])
---
--- Changing the type in the Emitter (The double fmap is cutting through the Cont and Emitter layers):
---
--- > glue toStdout <$.> fmap (fmap (pack . show)) (fromListE [1..3])
---
--- Changing the type in the committer (which is Contrvariant so needs to be a contramap):
---
--- > glue (contramap (pack . show) toStdout) <$.> fromListE [1..3]
---
--- Using the box version of glue:
---
--- > glueb <$.> (Box <$> pure toStdout <*> (fmap (pack . show) <$> fromListE [1..3]))
---
--- 2. fusion of a box, with an (a -> m (Maybe b)) function to allow for mapping, filtering and simple effects.
---
--- >>> let box' = Box <$> pure toStdout <*> fromListE ((pack . show) <$> [1..3])
--- >>> fuse (\a -> bool (pure $ Just $ "echo: " <> a) (pure Nothing) (a==("2"::Text))) <$.> box'
--- echo: 1
--- echo: 3
-
--- $commit
---
--- >>> commit toStdout "I'm committed!"
--- I'm committed!
--- True
---
--- Use mapC to modify a Committer and introduce effects.
---
--- >>> let c = mapC (\a -> if a==2 then (sleep 0.1 >> putStrLn "stole a 2!" >> sleep 0.1 >> pure (Nothing)) else (pure (Just a))) (contramap (pack . show) toStdout)
--- >>> glueb <$.> (Box <$> pure c <*> fromListE [1..3])
--- 1
--- stole a 2!
--- 3
---
--- The monoid instance of Committer sends each commit to both mappended committers. Because effects are also mappended together, the committed result is not always what is expected.
---
--- >>> let cFast = mapC (\b -> pure (Just b)) . contramap ("fast: " <>) $ toStdout
--- >>> let cSlow = mapC (\b -> sleep 0.1 >> pure (Just b)) . contramap ("slow: " <>) $ toStdout
--- >>> (glueb <$.> (Box <$> pure (cFast <> cSlow) <*> fromListE ((pack . show) <$> [1..3]))) <* sleep 1
--- fast: 1
--- slow: 1
--- fast: 2
--- slow: 2
--- fast: 3
--- slow: 3
---
--- To approximate what is intuitively expected, use 'concurrentC'.
---
--- >>> runCont $ (fromList_ ((pack . show) <$> [1..3]) <$> (concurrentC cFast cSlow)) <> pure (sleep 1)
--- fast: 1
--- fast: 2
--- fast: 3
--- slow: 1
--- slow: 2
--- slow: 3
---
--- This is all non-deterministic, hence the necessity for messy delays and heuristic avoidance of console races.
-
--- $emit
---
--- >>> ("I'm emitted!" :: Text) & Just & pure & Emitter & emit >>= print
--- Just "I'm emitted!"
---
--- >>> with (fromListE [1]) (\e' -> (emit e' & fmap show) >>= putStrLn & replicate 3 & sequence_)
--- Just 1
--- Nothing
--- Nothing
---
--- >>> toListE <$.> (fromListE [1..3])
+-- >>> import Prelude
+-- >>> pushList <$|> qList [1,2,3]
 -- [1,2,3]
 --
--- The monoid instance is left-biased.
---
--- >>> toListE <$.> (fromListE [1..3] <> fromListE [7..9])
--- [1,2,3,7,8,9]
---
--- Use concurrentE to get some nondeterministic balance.
---
--- > let es = (join $ concurrentE <$> (fromListE [1..3]) <*> (fromListE [7..9]))
--- > glue (contramap (pack . show) toStdout) <$.> es
--- 1
--- 2
--- 7
--- 3
--- 8
--- 9
-
--- $state
---
--- State committers and emitters are related as follows:
---
--- >>> toList . fst $ runIdentity $ flip execStateT (Seq.empty,Seq.fromList [1..4]) $ glue (hoist (zoom _1) stateC) (hoist (zoom _2) stateE)
--- [1,2,3,4]
---
--- For some reason, related to a lack of an MFunctor instance for Cont, but exactly not yet categorically pinned to a wall, the following compiles but is wrong.
---
--- >>> flip runStateT (Seq.empty) $ runCont $ glue <$> pure stateC <*> fromListE [1..4]
--- ((),fromList [])
-
--- $finite
---
--- Most committers and emitters will run forever until:
---
--- - the glued or fused other-side returns.
--- - the Transducer, stream or monadic action returns.
---
--- Finite ends (collective noun for emitters and committers) can be created with 'sink' and 'source' eg
---
--- >>> glue <$> contramap show <$> (sink 5 putStrLn) <*.> fromListE [1..]
--- 1
--- 2
--- 3
--- 4
--- 5
---
--- Two infinite ends will tend to run infinitely.
---
--- > glue <$> pure (contramap (pack . show) toStdout) <*.> fromListE [1..]
---
--- 1
--- 2
--- ...
--- 💁
--- ∞
---
--- Use glueN to create a finite computation.
---
--- >>> glueN 4 <$> pure (contramap (pack . show) toStdout) <*.> fromListE [1..]
--- 1
--- 2
--- 3
--- 4
+-- >>> glue toStdout <$|> qList ["a", "b", "c"]
+-- a
+-- b
+-- c
diff --git a/src/Box/Box.hs b/src/Box/Box.hs
--- a/src/Box/Box.hs
+++ b/src/Box/Box.hs
@@ -8,30 +8,29 @@
 {-# OPTIONS_GHC -Wall #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
 
--- | A box is something that commits and emits
+-- | A box is something that 'commit's and 'emit's
 module Box.Box
   ( Box (..),
+    CoBox,
+    CoBoxM (..),
     bmap,
-    hoistb,
+    foistb,
     glue,
-    glue_,
-    glueb,
+    glueN,
     fuse,
-    dotb,
     Divap (..),
     DecAlt (..),
+    cobox,
+    seqBox,
   )
 where
 
-import Box.Committer (Committer (commit), mapC)
-import Box.Emitter (Emitter (emit), mapE)
+import Box.Committer
+import Box.Emitter
 import Control.Applicative
   ( Alternative (empty, (<|>)),
     Applicative (liftA2),
   )
-import Control.Monad (when)
-import Control.Monad.Morph (MFunctor (hoist))
-import Data.Functor (($>))
 import Data.Functor.Contravariant (Contravariant (contramap))
 import Data.Functor.Contravariant.Divisible
   ( Decidable (choose, lose),
@@ -39,20 +38,35 @@
   )
 import Data.Profunctor (Profunctor (dimap))
 import Data.Void (Void, absurd)
-import Prelude
+import Prelude hiding ((.), id)
+import Box.Codensity
+import Control.Monad.State.Lazy
+import qualified Data.Sequence as Seq
+import Data.Bool
+import Data.Semigroupoid
+import Box.Functor
 
--- | A Box is a product of a Committer m and an Emitter. Think of a box with an incoming wire and an outgoing wire. Now notice that the abstraction is reversable: are you looking at two wires from "inside a box"; a blind erlang grunt communicating with the outside world via the two thin wires, or are you looking from "outside the box"; interacting with a black box object. Either way, it's a box.
--- And either way, the committer is contravariant and the emitter covariant so it forms a profunctor.
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Prelude
+-- >>> import Box
+-- >>> import Data.Text (pack)
+-- >>> import Data.Bool
+
+-- | A Box is a product of a 'Committer' and an 'Emitter'.
 --
--- a Box can also be seen as having an input tape and output tape, thus available for turing and finite-state machine metaphorics.
+-- Think of a box with an incoming arrow an outgoing arrow. And then make your pov ambiguous: are you looking at two wires from "inside a box"; or are you looking from "outside the box"; interacting with a black box object. Either way, it looks the same: it's a box.
+--
+-- And either way, one of the arrows, the 'Committer', is contravariant and the other, the 'Emitter' is covariant. The combination is a profunctor.
+--
 data Box m c e = Box
   { committer :: Committer m c,
     emitter :: Emitter m e
   }
 
--- | Wrong signature for the MFunctor class
-hoistb :: Monad m => (forall a. m a -> n a) -> Box m c e -> Box n c e
-hoistb nat (Box c e) = Box (hoist nat c) (hoist nat e)
+-- | Wrong kind signature for the FFunctor class
+foistb :: (forall a. m a -> n a) -> Box m c e -> Box n c e
+foistb nat (Box c e) = Box (foist nat c) (foist nat e)
 
 instance (Functor m) => Profunctor (Box m) where
   dimap f g (Box c e) = Box (contramap f c) (fmap g e)
@@ -64,56 +78,47 @@
   mempty = Box mempty mempty
   mappend = (<>)
 
--- | a profunctor dimapMaybe
+-- | A profunctor dimapMaybe
 bmap :: (Monad m) => (a' -> m (Maybe a)) -> (b -> m (Maybe b')) -> Box m a b -> Box m a' b'
-bmap fc fe (Box c e) = Box (mapC fc c) (mapE fe e)
-
-{-
-instance Category (Box Identity) where
-  id = Box ??? ???
-  (.) (Box c e) (Box c' e') = runIdentity $ glue c e' >> pure (Box c' e)
--}
-
--- | composition of monadic boxes
-dotb :: (Monad m) => Box m a b -> Box m b c -> m (Box m a c)
-dotb (Box c e) (Box c' e') = glue c' e $> Box c e'
+bmap fc fe (Box c e) = Box (witherC fc c) (witherE fe e)
 
 -- | Connect an emitter directly to a committer of the same type.
 --
--- The monadic action returns when the committer finishes.
+-- >>> glue showStdout <$|> qList [1..3]
+-- 1
+-- 2
+-- 3
 glue :: (Monad m) => Committer m a -> Emitter m a -> m ()
-glue c e = go
-  where
-    go = do
-      a <- emit e
-      c' <- maybe (pure False) (commit c) a
-      when c' go
+glue c e = fix $ \rec -> emit e >>= maybe (pure False) (commit c) >>= bool (pure ()) rec
 
--- | Connect an emitter directly to a committer of the same type.
+-- | Glues a committer and emitter, and takes n emits
 --
--- The monadic action returns if the committer returns False.
-glue_ :: (Monad m) => Committer m a -> Emitter m a -> m ()
-glue_ c e = go
-  where
-    go = do
-      a <- emit e
-      case a of
-        Nothing -> go
-        Just a' -> do
-          b <- commit c a'
-          if b then go else pure ()
-
--- | Short-circuit a homophonuos box.
-glueb :: (Monad m) => Box m a a -> m ()
-glueb (Box c e) = glue c e
+-- >>> glueN 3 <$> pure showStdout <*|> qList [1..]
+-- 1
+-- 2
+-- 3
+--
+-- Note that glueN counts the number of events passing across the connection and doesn't take into account post-transmission activity in the Committer, eg
+--
+-- >>> glueN 4 (witherC (\x -> bool (pure Nothing) (pure (Just x)) (even x)) showStdout) <$|> qList [0..9]
+-- 0
+-- 2
+--
+glueN :: Monad m => Int -> Committer m a -> Emitter m a -> m ()
+glueN n c e = flip evalStateT 0 $ glue (foist lift c) (takeE n e)
 
--- | fuse a box
+-- | Glue a Committer to an Emitter within a box.
 --
--- > fuse (pure . pure) == glueb
+-- > fuse (pure . pure) == \(Box c e) -> glue c e
+--
+-- A command-line echoer
+--
+-- > fuse (pure . Just . ("echo " <>)) (Box toStdout fromStdin)
+--
 fuse :: (Monad m) => (a -> m (Maybe b)) -> Box m b a -> m ()
-fuse f (Box c e) = glue c (mapE f e)
+fuse f (Box c e) = glue c (witherE f e)
 
--- | combines 'divide'/'conquer' and 'liftA2'/'pure'
+-- | combines 'divide' 'conquer' and 'liftA2' 'pure'
 class Divap p where
   divap :: (a -> (b, c)) -> ((d, e) -> f) -> p b d -> p c e -> p a f
   conpur :: a -> p b a
@@ -133,3 +138,36 @@
   choice split' merge (Box lc le) (Box rc re) =
     Box (choose split' lc rc) (fmap merge $ fmap Left le <|> fmap Right re)
   loss = Box (lose absurd) empty
+
+-- | A box continuation
+type CoBox m a b = Codensity m (Box m a b)
+
+-- | Construct a CoBox
+--
+--
+-- > cobox = Box <$> c <*> e
+--
+-- >>> fuse (pure . Just . ("show: " <>) . pack . show) <$|> (cobox (pure toStdout) (qList [1..3]))
+-- show: 1
+-- show: 2
+-- show: 3
+cobox :: CoCommitter m a -> CoEmitter m b -> CoBox m a b
+cobox c e = Box <$> c <*> e
+
+-- | State monad queue.
+seqBox :: (Monad m) => Box (StateT (Seq.Seq a) m) a a
+seqBox = Box push pop
+
+-- | cps composition of monadic boxes
+dotco :: Monad m => Codensity m (Box m a b) -> Codensity m (Box m b c) -> Codensity m (Box m a c)
+dotco b b' = lift $ do
+  (Box c e) <- lowerCodensity b
+  (Box c' e') <- lowerCodensity b'
+  glue c' e
+  pure (Box c e')
+
+-- | Wrapper for the semigroupoid instance of a box continuation.
+newtype CoBoxM m a b = CoBoxM { uncobox :: Codensity m (Box m a b) }
+
+instance (Monad m) => Semigroupoid (CoBoxM m) where
+  o (CoBoxM b) (CoBoxM b')= CoBoxM (dotco b' b)
diff --git a/src/Box/Codensity.hs b/src/Box/Codensity.hs
new file mode 100644
--- /dev/null
+++ b/src/Box/Codensity.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE RankNTypes #-}
+{-# OPTIONS_GHC -Wall #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+-- | Extra Codensity operators.
+module Box.Codensity
+  ( close,
+    process,
+    (<$|>),
+    (<*|>),
+    module Control.Monad.Codensity,
+  )
+where
+
+import Control.Applicative
+import Control.Monad.Codensity
+import Prelude
+
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Prelude
+-- >>> import Box
+-- >>> import Data.Bool
+
+instance (Semigroup a) => Semigroup (Codensity m a) where
+  (<>) = liftA2 (<>)
+
+instance (Functor m, Semigroup a, Monoid a) => Monoid (Codensity m a) where
+  mempty = pure mempty
+
+  mappend = (<>)
+
+-- | close a continuation
+--
+-- >>> close $ glue showStdout <$> qList [1..3]
+-- 1
+-- 2
+-- 3
+close :: Codensity m (m r) -> m r
+close x = runCodensity x id
+
+-- | fmap then close over a Codensity
+--
+-- >>> process (glue showStdout) (qList [1..3])
+-- 1
+-- 2
+-- 3
+process :: (a -> m r) -> Codensity m a -> m r
+process = flip runCodensity
+
+infixr 3 <$|>
+
+-- | fmap then close over a Codensity
+--
+-- >>> glue showStdout <$|> qList [1..3]
+-- 1
+-- 2
+-- 3
+(<$|>) :: (a -> m r) -> Codensity m a -> m r
+(<$|>) = process
+
+
+infixr 3 <*|>
+
+-- | apply to a continuation and close.
+--
+-- >>> glue <$> (pure showStdout) <*|> qList [1..3]
+-- 1
+-- 2
+-- 3
+(<*|>) :: Codensity m (a -> m r) -> Codensity m a -> m r
+(<*|>) f a = close (f <*> a)
diff --git a/src/Box/Committer.hs b/src/Box/Committer.hs
--- a/src/Box/Committer.hs
+++ b/src/Box/Committer.hs
@@ -11,33 +11,46 @@
 -- | `commit`
 module Box.Committer
   ( Committer (..),
-    drain,
-    mapC,
-    premapC,
-    postmapC,
-    stateC,
+    CoCommitter,
+    witherC,
     listC,
+    push,
   )
 where
 
-import Control.Monad.Morph
 import Control.Monad.State.Lazy
 import Data.Functor.Contravariant
 import Data.Functor.Contravariant.Divisible
 import qualified Data.Sequence as Seq
 import Data.Void
 import Prelude
+import Box.Codensity (Codensity)
+import Box.Functor
 
--- | a Committer a "commits" values of type a. A Sink and a Consumer are some other metaphors for this.
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Prelude
+-- >>> import Box
+-- >>> import Data.Bool
+
+-- | A Committer 'commit's values of type a and signals success or otherwise. A Sink and a Consumer are some other metaphors for this.
 --
 -- A Committer absorbs the value being committed; the value disappears into the opaque thing that is a Committer from the pov of usage.
+--
+-- >>> commit toStdout "I'm committed!"
+-- I'm committed!
+-- True
+--
 newtype Committer m a = Committer
   { commit :: a -> m Bool
   }
 
-instance MFunctor Committer where
-  hoist nat (Committer c) = Committer $ nat . c
+-- | 'Committer' continuation.
+type CoCommitter m a = Codensity m (Committer m a)
 
+instance FFunctor Committer where
+  foist nat (Committer c) = Committer $ nat . c
+
 instance (Applicative m) => Semigroup (Committer m a) where
   (<>) i1 i2 = Committer (\a -> (||) <$> commit i1 a <*> commit i2 a)
 
@@ -66,15 +79,14 @@
         Left b -> commit i1 b
         Right c -> commit i2 c
 
--- | Do nothing with values that are committed.
+-- | A monadic [Witherable](https://hackage.haskell.org/package/witherable)
 --
--- This is useful for keeping the commit end of a box or pipeline open.
-drain :: (Applicative m) => Committer m a
-drain = Committer (\_ -> pure True)
-
--- | This is a contramapMaybe, if such a thing existed, as the contravariant version of a mapMaybe.  See [witherable](https://hackage.haskell.org/package/witherable)
-mapC :: (Monad m) => (b -> m (Maybe a)) -> Committer m a -> Committer m b
-mapC f c = Committer go
+-- >>> glue (witherC (\x -> pure $ bool Nothing (Just x) (even x)) showStdout) <$|> qList [0..5]
+-- 0
+-- 2
+-- 4
+witherC :: (Monad m) => (b -> m (Maybe a)) -> Committer m a -> Committer m b
+witherC f c = Committer go
   where
     go b = do
       fb <- f b
@@ -82,34 +94,26 @@
         Nothing -> pure True
         Just fb' -> commit c fb'
 
--- | adds a monadic action to the committer
-premapC ::
-  (Applicative m) =>
-  (Committer m a -> m ()) ->
-  Committer m a ->
-  Committer m a
-premapC f c = Committer $ \a -> f c *> commit c a
-
--- | adds a post-commit monadic action to the committer
-postmapC ::
-  (Monad m) =>
-  (Committer m a -> m ()) ->
-  Committer m a ->
-  Committer m a
-postmapC f c = Committer $ \a -> do
-  r <- commit c a
-  f c
-  pure r
-
--- | commit to a StateT 'Seq'.
+-- | Convert a committer to be a list committer.  Think mconcat.
 --
--- Seq is used because only a finite number of commits are expected and because snoc'ing is cool.
-stateC :: (Monad m) => Committer (StateT (Seq.Seq a) m) a
-stateC = Committer $ \a -> do
-  modify (Seq.:|> a)
-  pure True
-
--- | list committer
+-- >>> glue showStdout <$|> qList [[1..3]]
+-- [1,2,3]
+--
+-- >>>  glue (listC showStdout) <$|> qList [[1..3]]
+-- 1
+-- 2
+-- 3
 listC :: (Monad m) => Committer m a -> Committer m [a]
 listC c = Committer $ \as ->
   or <$> sequence (commit c <$> as)
+
+-- | Push to a state sequence.
+--
+-- >>> import Control.Monad.State.Lazy
+-- >>> import qualified Data.Sequence as Seq
+-- >>> flip execStateT Seq.empty . glue push . foist lift <$|> qList [1..3]
+-- fromList [1,2,3]
+push :: (Monad m) => Committer (StateT (Seq.Seq a) m) a
+push = Committer $ \a -> do
+  modify (Seq.:|> a)
+  pure True
diff --git a/src/Box/Connectors.hs b/src/Box/Connectors.hs
--- a/src/Box/Connectors.hs
+++ b/src/Box/Connectors.hs
@@ -7,109 +7,74 @@
 {-# LANGUAGE TypeFamilies #-}
 {-# OPTIONS_GHC -Wall #-}
 
--- | various ways to connect things up
+-- | Various ways to connect things up.
 module Box.Connectors
-  ( fromListE,
-    fromList_,
-    toList_,
-    fromToList_,
-    emitQ,
-    commitQ,
+  ( qList,
+    popList,
+    pushList,
+    pushListN,
     sink,
     source,
     forkEmit,
-    feedback,
-    queueCommitter,
-    queueEmitter,
+    bufferCommitter,
+    bufferEmitter,
     concurrentE,
     concurrentC,
-    glueN,
   )
 where
 
 import Box.Box
 import Box.Committer
-import Box.Cont
+import Box.Codensity
 import Box.Emitter
 import Box.Queue
 import Control.Concurrent.Classy.Async as C
-import Control.Lens
 import Control.Monad.Conc.Class (MonadConc)
-import Control.Monad.Morph
 import Control.Monad.State.Lazy
 import Data.Foldable
 import qualified Data.Sequence as Seq
 import Prelude
+import Box.Functor
 
 -- $setup
 -- >>> :set -XOverloadedStrings
--- >>> :set -XGADTs
--- >>> :set -XFlexibleContexts
--- >>> import Data.Functor.Contravariant
 -- >>> import Box
--- >>> import Control.Applicative
--- >>> import Control.Monad.Conc.Class as C
--- >>> import Control.Lens
--- >>> import qualified Data.Sequence as Seq
--- >>> import Data.Text (pack, Text)
--- >>> import Data.Functor.Contravariant
-
--- | Turn a list into an 'Emitter' continuation via a 'Queue'
-fromListE :: (MonadConc m) => [a] -> Cont m (Emitter m a)
-fromListE xs = Cont $ queueE (eListC (Emitter . pure . Just <$> xs))
-
-eListC :: (Monad m) => [Emitter m a] -> Committer m a -> m ()
-eListC [] _ = pure ()
-eListC (e : es) c = do
-  x <- emit e
-  case x of
-    Nothing -> pure ()
-    Just x' -> commit c x' *> eListC es c
-
--- | fromList_ directly supplies to a committer action
---
--- FIXME: fromList_ combined with cRef is failing dejavu concurrency testing...
-fromList_ :: Monad m => [a] -> Committer m a -> m ()
-fromList_ xs c = flip evalStateT (Seq.fromList xs) $ glue (hoist lift c) stateE
+-- >>> import Prelude
+-- >>> import Data.Bool
+-- >>> import Control.Monad
 
--- | toList_ directly receives from an emitter
+-- | Queue a list.
 --
--- TODO: check isomorphism
+-- >>> pushList <$|> qList [1,2,3]
+-- [1,2,3]
 --
--- > toList_ == toListE
-toList_ :: (Monad m) => Emitter m a -> m [a]
-toList_ e = toList <$> flip execStateT Seq.empty (glue stateC (hoist lift e))
+qList :: (MonadConc m) => [a] -> CoEmitter m a
+qList xs = emitQ Unbounded (\c -> fmap and (traverse (commit c) xs))
 
--- | Glues a committer and emitter, taking n emits
+-- | Directly supply a list to a committer action, via pop.
 --
--- >>> glueN 4 <$> pure (contramap (pack . show) toStdout) <*.> fromListE [1..]
+-- >>> popList [1..3] showStdout
 -- 1
 -- 2
 -- 3
--- 4
-glueN :: Monad m => Int -> Committer m a -> Emitter m a -> m ()
-glueN n c e = flip evalStateT 0 $ glue (hoist lift c) (takeE n e)
+popList :: Monad m => [a] -> Committer m a -> m ()
+popList xs c = flip evalStateT (Seq.fromList xs) $ glue (foist lift c) pop
 
--- | take a list, emit it through a box, and output the committed result.
+-- | Push an Emitter into a list, via push.
 --
--- The pure nature of this computation is highly useful for testing,
--- especially where parts of the box under investigation has non-deterministic attributes.
-fromToList_ :: (Monad m) => [a] -> (Box (StateT (Seq.Seq b, Seq.Seq a) m) b a -> StateT (Seq.Seq b, Seq.Seq a) m r) -> m [b]
-fromToList_ xs f = do
-  (res, _) <-
-    flip execStateT (Seq.empty, Seq.fromList xs) $
-      f (Box (hoist (zoom _1) stateC) (hoist (zoom _2) stateE))
-  pure $ toList res
-
--- | hook a committer action to a queue, creating an emitter continuation
-emitQ :: (MonadConc m) => (Committer m a -> m r) -> Cont m (Emitter m a)
-emitQ cio = Cont $ \eio -> queueE cio eio
+-- >>> pushList <$|> qList [1..3]
+-- [1,2,3]
+pushList :: (Monad m) => Emitter m a -> m [a]
+pushList e = toList <$> flip execStateT Seq.empty (glue push (foist lift e))
 
--- | hook a committer action to a queue, creating an emitter continuation
-commitQ :: (MonadConc m) => (Emitter m a -> m r) -> Cont m (Committer m a)
-commitQ eio = Cont $ \cio -> queueC cio eio
+-- | Push an Emitter into a list, finitely.
+--
+-- >>> pushListN 2 <$|> qList [1..3]
+-- [1,2]
+pushListN :: (Monad m) => Int -> Emitter m a -> m [a]
+pushListN n e = toList <$> flip execStateT Seq.empty (glueN n push (foist lift e))
 
--- | singleton sink
+-- singleton sink
 sink1 :: (Monad m) => (a -> m ()) -> Emitter m a -> m ()
 sink1 f e = do
   a <- emit e
@@ -117,21 +82,37 @@
     Nothing -> pure ()
     Just a' -> f a'
 
--- | finite sink
-sink :: (MonadConc m) => Int -> (a -> m ()) -> Cont m (Committer m a)
-sink n f = commitQ $ replicateM_ n . sink1 f
+-- | Create a finite Committer.
+--
+-- >>> glue <$> sink 2 print <*|> qList [1..3]
+-- 1
+-- 2
+sink :: (MonadConc m) => Int -> (a -> m ()) -> CoCommitter m a
+sink n f = commitQ Unbounded $ replicateM_ n . sink1 f
 
--- | singleton source
+-- singleton source
 source1 :: (Monad m) => m a -> Committer m a -> m ()
 source1 a c = do
   a' <- a
   void $ commit c a'
 
--- | finite source
-source :: (MonadConc m) => Int -> m a -> Cont m (Emitter m a)
-source n f = emitQ $ replicateM_ n . source1 f
+-- | Create a finite Emitter.
+--
+-- >>> glue toStdout <$|> source 2 (pure "hi")
+-- hi
+-- hi
+source :: (MonadConc m) => Int -> m a -> CoEmitter m a
+source n f = emitQ Unbounded $ replicateM_ n . source1 f
 
--- | glues an emitter to a committer, then resupplies the emitter
+-- | Glues an emitter to a committer, then resupplies the emitter.
+--
+-- >>> (c1,l1) <- refCommitter :: IO (Committer IO Int, IO [Int])
+-- >>> close $ toListM <$> (forkEmit <$> (qList [1..3]) <*> pure c1)
+-- [1,2,3]
+--
+-- >>> l1
+-- [1,2,3]
+--
 forkEmit :: (Monad m) => Emitter m a -> Committer m a -> Emitter m a
 forkEmit e c =
   Emitter $ do
@@ -139,45 +120,62 @@
     maybe (pure ()) (void <$> commit c) a
     pure a
 
--- | fuse a committer to a buffer
-queueCommitter :: (MonadConc m) => Committer m a -> Cont m (Committer m a)
-queueCommitter c = Cont $ \caction -> queueC caction (glue c)
+-- | Buffer a committer.
+bufferCommitter :: (MonadConc m) => Committer m a -> CoCommitter m a
+bufferCommitter c = Codensity $ \caction -> queueL Unbounded caction (glue c)
 
--- | fuse an emitter to a buffer
-queueEmitter :: (MonadConc m) => Emitter m a -> Cont m (Emitter m a)
-queueEmitter e = Cont $ \eaction -> queueE (`glue` e) eaction
+-- | Buffer an emitter.
+bufferEmitter :: (MonadConc m) => Emitter m a -> CoEmitter m a
+bufferEmitter e = Codensity $ \eaction -> queueR Unbounded (`glue` e) eaction
 
--- | concurrently run two emitters
+-- | Concurrently run two emitters.
 --
--- This differs from mappend in that the monoidal (and alternative) instance of an Emitter is left-biased (The left emitter exhausts before the right one is begun). This is non-deterministically concurrent.
-concurrentE ::
-  (MonadConc m) =>
-  Emitter m a ->
-  Emitter m a ->
-  Cont m (Emitter m a)
-concurrentE e e' =
-  Cont $ \eaction ->
-    fst
-      <$> C.concurrently
-        (queueE (`glue` e) eaction)
-        (queueE (`glue` e') eaction)
+-- This differs to (<>), which is left-biased.
+--
+-- Note that functions such as toListM, which complete on the first Nothing emitted, will not work as expected.
+--
+-- >>> close $ (fmap toListM) (join $ concurrentE Single <$> qList [1..3] <*> qList [5..9])
+-- [1,2,3]
+--
+-- In the code below, the ordering is non-deterministic.
+--
+-- > (c,l) <- refCommitter :: IO (Committer IO Int, IO [Int])
+-- > close $ glue c <$> (join $ concurrentE Single <$> qList [1..30] <*> qList [40..60])
+--
+concurrentE :: MonadConc f =>
+  Queue a -> Emitter f a -> Emitter f a -> CoEmitter f a
+concurrentE q e e' =
+  Codensity $ \eaction -> snd . fst <$> C.concurrently (queue q (`glue` e) eaction) (queue q (`glue` e') eaction)
 
--- | run two committers concurrently
-concurrentC :: (MonadConc m) => Committer m a -> Committer m a -> Cont m (Committer m a)
-concurrentC c c' = mergeC <$> eitherC c c'
+-- | Concurrently run two committers.
+--
+-- >>> import Data.Functor.Contravariant
+-- >>> import Data.Text (pack)
+-- >>> cFast = witherC (\b -> pure (Just b)) . contramap ("fast: " <>) $ toStdout
+-- >>> cSlow = witherC (\b -> sleep 0.1 >> pure (Just b)) . contramap ("slow: " <>) $ toStdout
+-- >>> close $ (popList ((pack . show) <$> [1..3]) <$> (concurrentC Unbounded cFast cSlow)) <> pure (sleep 1)
+-- fast: 1
+-- fast: 2
+-- fast: 3
+-- slow: 1
+-- slow: 2
+-- slow: 3
+concurrentC :: (MonadConc m) => Queue a -> Committer m a -> Committer m a -> CoCommitter m a
+concurrentC q c c' = mergeC <$> eitherC q c c'
 
 eitherC ::
   (MonadConc m) =>
+  Queue a ->
   Committer m a ->
   Committer m a ->
-  Cont m (Either (Committer m a) (Committer m a))
-eitherC cl cr =
-  Cont $
+  Codensity m (Either (Committer m a) (Committer m a))
+eitherC q cl cr =
+  Codensity $
     \kk ->
       fst
         <$> C.concurrently
-          (queueC (kk . Left) (glue cl))
-          (queueC (kk . Right) (glue cr))
+          (queueL q (kk . Left) (glue cl))
+          (queueL q (kk . Right) (glue cr))
 
 mergeC :: Either (Committer m a) (Committer m a) -> Committer m a
 mergeC ec =
@@ -185,15 +183,3 @@
     case ec of
       Left lc -> commit lc a
       Right rc -> commit rc a
-
--- | a box modifier that feeds commits back to the emitter
-feedback ::
-  (MonadConc m) =>
-  (a -> m (Maybe b)) ->
-  Cont m (Box m b a) ->
-  Cont m (Box m b a)
-feedback f box =
-  Cont $ \bio ->
-    with box $ \(Box c e) -> do
-      glue c (mapE f e)
-      bio (Box c e)
diff --git a/src/Box/Cont.hs b/src/Box/Cont.hs
deleted file mode 100644
--- a/src/Box/Cont.hs
+++ /dev/null
@@ -1,112 +0,0 @@
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE RankNTypes #-}
-{-# OPTIONS_GHC -Wall #-}
-
--- | A continuation type.
-module Box.Cont
-  ( Cont (..),
-    runCont,
-    (<$.>),
-    (<*.>),
-    Cont_ (..),
-    runCont_,
-  )
-where
-
-import Control.Applicative
-import Control.Monad.IO.Class
-import Prelude
-
--- | A continuation similar to ` Control.Monad.ContT` but where the result type is swallowed by an existential
-newtype Cont m a = Cont
-  { with :: forall r. (a -> m r) -> m r
-  }
-
-instance Functor (Cont m) where
-  fmap f mx = Cont (\return_ -> mx `with` \x -> return_ (f x))
-
-instance Applicative (Cont m) where
-  pure r = Cont (\return_ -> return_ r)
-
-  mf <*> mx = Cont (\return_ -> mf `with` \f -> mx `with` \x -> return_ (f x))
-
-instance Monad (Cont m) where
-  return r = Cont (\return_ -> return_ r)
-
-  ma >>= f = Cont (\return_ -> ma `with` \a -> f a `with` \b -> return_ b)
-
-instance (MonadIO m) => MonadIO (Cont m) where
-  liftIO m =
-    Cont
-      ( \return_ -> do
-          a <- liftIO m
-          return_ a
-      )
-
-instance (Semigroup a) => Semigroup (Cont m a) where
-  (<>) = liftA2 (<>)
-
-instance (Functor m, Semigroup a, Monoid a) => Monoid (Cont m a) where
-  mempty = pure mempty
-
-  mappend = (<>)
-
--- | finally run a continuation
-runCont :: Cont m (m r) -> m r
-runCont x = with x id
-
--- | sometimes you have no choice but to void it up
-newtype Cont_ m a = Cont_
-  { with_ :: (a -> m ()) -> m ()
-  }
-
-instance Functor (Cont_ m) where
-  fmap f mx = Cont_ (\return_ -> mx `with_` \x -> return_ (f x))
-
-instance Applicative (Cont_ m) where
-  pure r = Cont_ (\return_ -> return_ r)
-
-  mf <*> mx = Cont_ (\return_ -> mf `with_` \f -> mx `with_` \x -> return_ (f x))
-
-instance Monad (Cont_ m) where
-  return r = Cont_ (\return_ -> return_ r)
-
-  ma >>= f = Cont_ (\return_ -> ma `with_` \a -> f a `with_` \b -> return_ b)
-
-instance (MonadIO m) => MonadIO (Cont_ m) where
-  liftIO m =
-    Cont_
-      ( \return_ -> do
-          a <- liftIO m
-          return_ a
-      )
-
-instance (Semigroup a) => Semigroup (Cont_ m a) where
-  (<>) = liftA2 (<>)
-
-instance (Functor m, Semigroup a, Monoid a) => Monoid (Cont_ m a) where
-  mempty = pure mempty
-
-  mappend = (<>)
-
--- | finally run a Cont_
-runCont_ :: Cont_ m (m ()) -> m ()
-runCont_ x = with_ x id
-
-infixr 3 <$.>
-
--- | fmap over a continuation and then run the result.
---
--- The . position is towards the continuation
-(<$.>) :: (a -> m r) -> Cont m a -> m r
-(<$.>) f a = runCont (fmap f a)
-
-infixr 3 <*.>
-
--- | fmap over a continuation and then run the result.
---
--- The . position is towards the continuation
-(<*.>) :: Cont m (a -> m r) -> Cont m a -> m r
-(<*.>) f a = runCont (f <*> a)
diff --git a/src/Box/Emitter.hs b/src/Box/Emitter.hs
--- a/src/Box/Emitter.hs
+++ b/src/Box/Emitter.hs
@@ -7,47 +7,59 @@
 {-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_GHC -Wall #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
 
 -- | `emit`
 module Box.Emitter
   ( Emitter (..),
-    mapE,
+    type CoEmitter,
+    toListM,
+    witherE,
     readE,
-    readE_,
-    parseE,
-    parseE_,
-    premapE,
-    postmapE,
-    postmapM,
-    toListE,
     unlistE,
-    stateE,
     takeE,
     takeUntilE,
-    filterE,
-  )
+    pop,
+)
 where
 
 import Control.Applicative
-import Control.Monad.Morph
 import Control.Monad.State.Lazy
-import qualified Data.Attoparsec.Text as A
 import Data.Bool
-import Data.Foldable
-import qualified Data.Sequence as Seq
 import Data.Text (Text, pack, unpack)
 import Prelude
+import Control.Monad.Codensity
+import Box.Functor
+import qualified Data.Sequence as Seq
+import qualified Data.DList as D
 
--- | an `Emitter` "emits" values of type a. A Source & a Producer (of a's) are the two other alternative but overloaded metaphors out there.
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Prelude
+-- >>> import Box
+-- >>> import Data.Bool
+-- >>> import Data.Text (Text)
+
+-- | an `Emitter` `emit`s values of type Maybe a. Source & Producer are also appropriate metaphors.
 --
--- An Emitter "reaches into itself" for the value to emit, where itself is an opaque thing from the pov of usage.  An Emitter is named for its main action: it emits.
+-- An Emitter reaches into itself for the value to emit, where itself is an opaque thing from the pov of usage.
+--
+-- >>> e = Emitter (pure (Just "I'm emitted"))
+-- >>> emit e
+-- Just "I'm emitted"
+--
+-- >>> emit mempty
+-- Nothing
 newtype Emitter m a = Emitter
   { emit :: m (Maybe a)
   }
 
-instance MFunctor Emitter where
-  hoist nat (Emitter e) = Emitter (nat e)
+-- | An 'Emitter' continuation.
+type CoEmitter m a = Codensity m (Emitter m a)
 
+instance FFunctor Emitter where
+  foist nat (Emitter e) = Emitter (nat e)
+
 instance (Functor m) => Functor (Emitter m) where
   fmap f m = Emitter (fmap (fmap f) (emit m))
 
@@ -76,6 +88,9 @@
         Nothing -> emit i
         Just a -> pure (Just a)
 
+  -- | Zero or more.
+  many e = Emitter $ Just <$> toListM e
+
 instance (Alternative m, Monad m) => MonadPlus (Emitter m) where
   mzero = empty
 
@@ -89,9 +104,27 @@
 
   mappend = (<>)
 
--- | like a monadic mapMaybe. (See [witherable](https://hackage.haskell.org/package/witherable))
-mapE :: (Monad m) => (a -> m (Maybe b)) -> Emitter m a -> Emitter m b
-mapE f e = Emitter go
+
+-- | This fold completes on the first Nothing emitted, which may not be what you want.
+instance FoldableM Emitter where
+  foldrM acc begin e =
+    maybe begin (\a' -> foldrM acc (acc a' begin) e) =<< emit e
+
+-- | Collect emits into a list, and close on the first Nothing.
+--
+-- >>> toListM <$|> qList [1..3]
+-- [1,2,3]
+toListM :: (Monad m) => Emitter m a -> m [a]
+toListM e = D.toList <$> foldrM (\a acc -> fmap (`D.snoc` a) acc) (pure D.empty) e
+
+-- | A monadic [Witherable](https://hackage.haskell.org/package/witherable)
+--
+-- >>> close $ toListM <$> witherE (\x -> bool (print x >> pure Nothing) (pure (Just x)) (even x)) <$> (qList [1..3])
+-- 1
+-- 3
+-- [2]
+witherE :: (Monad m) => (a -> m (Maybe b)) -> Emitter m a -> Emitter m b
+witherE f e = Emitter go
   where
     go = do
       a <- emit e
@@ -103,15 +136,10 @@
             Nothing -> go
             Just fa' -> pure (Just fa')
 
--- | parse emitter which returns the original text on failure
-parseE :: (Functor m) => A.Parser a -> Emitter m Text -> Emitter m (Either Text a)
-parseE parser e = (\t -> either (const $ Left t) Right (A.parseOnly parser t)) <$> e
-
--- | no error-reporting parsing
-parseE_ :: (Monad m) => A.Parser a -> Emitter m Text -> Emitter m a
-parseE_ parser = mapE (pure . either (const Nothing) Just) . parseE parser
-
--- | read parse emitter, returning the original string on error
+-- | Read parse 'Emitter', returning the original text on error
+--
+-- >>> process (toListM . readE) (qList ["1","2","3","four"]) :: IO [Either Text Int]
+-- [Right 1,Right 2,Right 3,Left "four"]
 readE ::
   (Functor m, Read a) =>
   Emitter m Text ->
@@ -123,97 +151,42 @@
         [(a, "")] -> Right a
         _err -> Left (pack str)
 
--- | no error-reporting reading
-readE_ ::
-  (Monad m, Read a) =>
-  Emitter m Text ->
-  Emitter m a
-readE_ = mapE (pure . either (const Nothing) Just) . readE
-
--- | adds a pre-emit monadic action to the emitter
-premapE ::
-  (Applicative m) =>
-  (Emitter m a -> m ()) ->
-  Emitter m a ->
-  Emitter m a
-premapE f e = Emitter $ f e *> emit e
-
--- | adds a post-emit monadic action to the emitter
-postmapE ::
-  (Monad m) =>
-  (Emitter m a -> m ()) ->
-  Emitter m a ->
-  Emitter m a
-postmapE f e = Emitter $ do
-  r <- emit e
-  f e
-  pure r
-
--- | add a post-emit monadic action on the emitted value (if there was any)
-postmapM ::
-  (Monad m) =>
-  (a -> m ()) ->
-  Emitter m a ->
-  Emitter m a
-postmapM f e = Emitter $ do
-  r <- emit e
-  case r of
-    Nothing -> pure Nothing
-    Just r' -> do
-      f r'
-      pure (Just r')
-
--- | turn an emitter into a list
-toListE :: (Monad m) => Emitter m a -> m [a]
-toListE e = go Seq.empty e
-  where
-    go xs e' = do
-      x <- emit e'
-      case x of
-        Nothing -> pure (toList xs)
-        Just x' -> go (xs Seq.:|> x') e'
-
--- | emit from a StateT Seq
---
--- FIXME: This compiles but is an infinite "a" emitter:
+-- | Convert a list emitter to a (Stateful) element emitter.
 --
--- let e1 = hoist (flip evalStateT (Seq.fromList ["a", "b"::Text])) stateE :: Emitter IO Text
-stateE :: (Monad m) => Emitter (StateT (Seq.Seq a) m) a
-stateE = Emitter $ do
-  xs' <- get
-  case xs' of
-    Seq.Empty -> pure Nothing
-    (x Seq.:<| xs'') -> do
-      put xs''
-      pure $ Just x
-
--- | convert a list emitter to a Stateful element emitter
+-- >>> import Control.Monad.State.Lazy
+-- >>> close $ flip runStateT [] . toListM . unlistE <$> (qList [[0..3],[5..7]])
+-- ([0,1,2,3,5,6,7],[])
 unlistE :: (Monad m) => Emitter m [a] -> Emitter (StateT [a] m) a
-unlistE es = mapE unlistS (hoist lift es)
+unlistE es = Emitter unlists
   where
-    unlistS xs = do
-      rs <- get
-      case rs <> xs of
-        [] -> pure Nothing
-        (x : xs') -> do
-          put xs'
-          pure (Just x)
+  -- unlists :: (Monad m) => StateT [a] m (Maybe a)
+  unlists = do
+    rs <- get
+    case rs of
+      [] -> do
+        xs <- lift $ emit es
+        case xs of
+          Nothing -> pure Nothing
+          Just xs' -> do
+            put xs'
+            unlists
+      (x:rs') -> do
+        put rs'
+        pure (Just x)
 
--- | Stop an 'Emitter' after n 'emit's
+-- | Take n emits.
+--
+-- >>> import Control.Monad.State.Lazy
+-- >>> close $ flip evalStateT 0 <$> toListM . takeE 4 <$> qList [0..]
+-- [0,1,2,3]
 takeE :: (Monad m) => Int -> Emitter m a -> Emitter (StateT Int m) a
-takeE n e = Emitter $ do
-  x <- emit (hoist lift e)
-  case x of
-    Nothing -> pure Nothing
-    Just x' -> do
-      n' <- get
-      bool (pure Nothing) (emit' n') (n' < n)
-      where
-        emit' n' = do
-          put (n' + 1)
-          pure $ Just x'
+takeE n (Emitter e) =
+  Emitter $ get >>= \n' -> bool (pure Nothing) (put (n'+1) >> lift e) (n'<n)
 
--- | Take from an emitter until predicate
+-- | Take from an emitter until a predicate.
+--
+-- >>> process (toListM . takeUntilE (==3)) (qList [0..])
+-- [0,1,2]
 takeUntilE :: (Monad m) => (a -> Bool) -> Emitter m a -> Emitter m a
 takeUntilE p e = Emitter $ do
   x <- emit e
@@ -222,13 +195,17 @@
     Just x' ->
       bool (pure (Just x')) (pure Nothing) (p x')
 
--- | Filter emissions according to a predicate.
-filterE :: (Monad m) => (a -> Bool) -> Emitter m a -> Emitter m a
-filterE p e = Emitter go
-  where
-    go = do
-      x <- emit e
-      case x of
-        Nothing -> pure Nothing
-        Just x' ->
-          bool go (pure (Just x')) (p x')
+-- | Pop from a State sequence.
+--
+-- >>> import qualified Data.Sequence as Seq
+-- >>> import Control.Monad.State.Lazy (evalStateT)
+-- >>> flip evalStateT (Seq.fromList [1..3]) $ toListM pop
+-- [1,2,3]
+pop :: (Monad m) => Emitter (StateT (Seq.Seq a) m) a
+pop = Emitter $ do
+  xs <- get
+  case xs of
+    Seq.Empty -> pure Nothing
+    (x Seq.:<| xs') -> do
+      put xs'
+      pure (Just x)
diff --git a/src/Box/Functor.hs b/src/Box/Functor.hs
new file mode 100644
--- /dev/null
+++ b/src/Box/Functor.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE AllowAmbiguousTypes #-}
+-- |
+
+module Box.Functor
+  (
+    FFunctor (..),
+    FoldableM (..),
+  ) where
+
+import Prelude
+import Data.Kind
+
+-- | An endofunctor in the category of endofunctors.
+--
+--  Like `Control.Monad.Morph.MFunctor` but without a `Monad` constraint.
+class FFunctor (h :: (Type -> Type) -> Type -> Type) where
+  foist :: (forall x. f x -> g x) -> h f a -> h g a
+
+-- | Monadically Foldable
+class FoldableM (t :: (Type -> Type) -> Type -> Type) where
+  foldrM :: (Monad m) => (a -> m b -> m b) -> m b -> t m a -> m b
diff --git a/src/Box/IO.hs b/src/Box/IO.hs
--- a/src/Box/IO.hs
+++ b/src/Box/IO.hs
@@ -8,7 +8,7 @@
 {-# OPTIONS_GHC -Wall #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
 
--- | IO actions
+-- | IO effects
 module Box.IO
   ( fromStdin,
     toStdout,
@@ -18,8 +18,8 @@
     showStdout,
     handleE,
     handleC,
-    cRef,
-    eRef,
+    refCommitter,
+    refEmitter,
     fileE,
     fileWriteC,
     fileAppendC,
@@ -28,11 +28,10 @@
 
 import Box.Committer
 import Box.Connectors
-import Box.Cont
+import Box.Codensity
 import Box.Emitter
 import qualified Control.Concurrent.Classy.IORef as C
 import Control.Exception
-import Control.Lens hiding ((.>), (:>), (<|), (|>))
 import qualified Control.Monad.Conc.Class as C
 import Data.Bool
 import Data.Foldable
@@ -41,30 +40,29 @@
 import Data.Text.IO as Text
 import System.IO as IO
 import Prelude
+import Data.Functor.Contravariant
 
 -- $setup
 -- >>> :set -XOverloadedStrings
--- >>> :set -XGADTs
--- >>> :set -XFlexibleContexts
--- >>> import Data.Functor.Contravariant
+-- >>> import Prelude
 -- >>> import Box
--- >>> import Control.Applicative
--- >>> import Control.Monad.Conc.Class as C
--- >>> import Control.Lens
--- >>> import qualified Data.Sequence as Seq
--- >>> import Data.Text (pack, Text)
+-- >>> import Data.Bool
+-- >>> import Data.Text (Text, pack)
 -- >>> import Data.Functor.Contravariant
 
 -- * console
 
--- | emit Text from stdin inputs
+-- | Emit text from stdin
 --
--- >>> :t emit fromStdin
--- emit fromStdin :: IO (Maybe Text)
+-- @
+-- λ> emit fromStdin
+-- hello
+-- Just "hello"
+-- @
 fromStdin :: Emitter IO Text
 fromStdin = Emitter $ Just <$> Text.getLine
 
--- | commit to stdout
+-- | Commit to stdout
 --
 -- >>> commit toStdout ("I'm committed!" :: Text)
 -- I'm committed!
@@ -72,24 +70,44 @@
 toStdout :: Committer IO Text
 toStdout = Committer $ \a -> Text.putStrLn a >> pure True
 
--- | finite console emitter
-fromStdinN :: Int -> Cont IO (Emitter IO Text)
+-- | Finite console emitter
+--
+-- @
+-- λ> toListM /<$|/> fromStdinN 2
+-- hello
+-- hello again
+-- ["hello","hello again"]
+-- @
+fromStdinN :: Int -> CoEmitter IO Text
 fromStdinN n = source n Text.getLine
 
--- | finite console committer
-toStdoutN :: Int -> Cont IO (Committer IO Text)
+-- | Finite console committer
+--
+-- >>> glue <$> contramap (pack . show) <$> (toStdoutN 2) <*|> qList [1..3]
+-- 1
+-- 2
+toStdoutN :: Int -> CoCommitter IO Text
 toStdoutN n = sink n Text.putStrLn
 
--- | read from console, throwing away read errors
+-- | Read from console, throwing away read errors
+--
+-- λ> glueN 2 showStdout (readStdin :: Emitter IO Int)
+-- 1
+-- 1
+-- hippo
+-- 2
 readStdin :: Read a => Emitter IO a
-readStdin = mapE (pure . either (const Nothing) Just) . readE $ fromStdin
+readStdin = witherE (pure . either (const Nothing) Just) . readE $ fromStdin
 
--- | show to stdout
+-- | Show to stdout
+--
+-- >>> glue showStdout <$|> qList [1..3]
+-- 1
+-- 2
+-- 3
 showStdout :: Show a => Committer IO a
 showStdout = contramap (Text.pack . show) toStdout
 
--- * handle operations
-
 -- | Emits lines of Text from a handle.
 handleE :: Handle -> Emitter IO Text
 handleE h = Emitter $ do
@@ -104,21 +122,27 @@
   Text.hPutStrLn h a
   pure True
 
--- | Emits lines of Text from a file.
-fileE :: FilePath -> Cont IO (Emitter IO Text)
-fileE fp = Cont $ \eio -> withFile fp ReadMode (eio . handleE)
+-- | Emit lines of Text from a file.
+fileE :: FilePath -> CoEmitter IO Text
+fileE fp = Codensity $ \eio -> withFile fp ReadMode (eio . handleE)
 
--- | Commits lines of Text to a file.
-fileWriteC :: FilePath -> Cont IO (Committer IO Text)
-fileWriteC fp = Cont $ \cio -> withFile fp WriteMode (cio . handleC)
+-- | Commit lines of Text to a file.
+fileWriteC :: FilePath -> CoCommitter IO Text
+fileWriteC fp = Codensity $ \cio -> withFile fp WriteMode (cio . handleC)
 
--- | Commits lines of Text, appending to a file.
-fileAppendC :: FilePath -> Cont IO (Committer IO Text)
-fileAppendC fp = Cont $ \cio -> withFile fp AppendMode (cio . handleC)
+-- | Commit lines of Text, appending to a file.
+fileAppendC :: FilePath -> CoCommitter IO Text
+fileAppendC fp = Codensity $ \cio -> withFile fp AppendMode (cio . handleC)
 
--- | commit to an IORef
-cRef :: (C.MonadConc m) => m (Committer m a, m [a])
-cRef = do
+-- | Commit to an IORef
+--
+-- >>> (c1,l1) <- refCommitter :: IO (Committer IO Int, IO [Int])
+-- >>> glue c1 <$|> qList [1..3]
+-- >>> l1
+-- [1,2,3]
+--
+refCommitter :: (C.MonadConc m) => m (Committer m a, m [a])
+refCommitter = do
   ref <- C.newIORef Seq.empty
   let c = Committer $ \a -> do
         C.modifyIORef ref (Seq.:|> a)
@@ -126,9 +150,13 @@
   let res = toList <$> C.readIORef ref
   pure (c, res)
 
--- | emit from a list IORef
-eRef :: (C.MonadConc m) => [a] -> m (Emitter m a)
-eRef xs = do
+-- | Emit from a list IORef
+--
+-- >>> e <- refEmitter [1..3]
+-- >>> toListM e
+-- [1,2,3]
+refEmitter :: (C.MonadConc m) => [a] -> m (Emitter m a)
+refEmitter xs = do
   ref <- C.newIORef xs
   let e = Emitter $ do
         as <- C.readIORef ref
diff --git a/src/Box/Queue.hs b/src/Box/Queue.hs
--- a/src/Box/Queue.hs
+++ b/src/Box/Queue.hs
@@ -9,38 +9,35 @@
 {-# OPTIONS_GHC -Wall #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
 
--- | queues
--- Follows [pipes-concurrency](https://hackage.haskell.org/package/pipes-concurrency)
+-- | STM Queues, based originally on [pipes-concurrency](https://hackage.haskell.org/package/pipes-concurrency)
 module Box.Queue
   ( Queue (..),
-    queueC,
-    queueE,
-    waitCancel,
-    ends,
-    withQE,
-    withQC,
-    toBox,
-    toBoxM,
-    liftB,
-    concurrentlyLeft,
-    concurrentlyRight,
+    queueL,
+    queueR,
+    queue,
     fromAction,
-    fuseActions,
+    emitQ,
+    commitQ,
   )
 where
 
 import Box.Box
 import Box.Committer
-import Box.Cont
+import Box.Codensity
 import Box.Emitter
 import Control.Applicative
 import Control.Concurrent.Classy.Async as C
 import Control.Concurrent.Classy.STM as C
 import Control.Monad.Catch as C
 import Control.Monad.Conc.Class as C
-import Control.Monad.Morph
 import Prelude
+import Box.Functor
 
+-- $setup
+-- >>> :set -XOverloadedStrings
+-- >>> import Box
+-- >>> import Prelude
+
 -- | 'Queue' specifies how messages are queued
 data Queue a
   = Unbounded
@@ -95,11 +92,11 @@
         )
 
 -- | turn a queue into a box (and a seal)
-toBox ::
+toBoxSTM ::
   (MonadSTM stm) =>
   Queue a ->
   stm (Box stm a a, stm ())
-toBox q = do
+toBoxSTM q = do
   (i, o) <- ends q
   sealed <- newTVarN "sealed" False
   let seal = writeTVar sealed True
@@ -116,18 +113,9 @@
   Queue a ->
   m (Box m a a, m ())
 toBoxM q = do
-  (b, s) <- atomically $ toBox q
+  (b, s) <- atomically $ toBoxSTM q
   pure (liftB b, atomically s)
 
--- | wait for the first action, and then cancel the second
-waitCancel :: (MonadConc m) => m b -> m a -> m b
-waitCancel a b =
-  C.withAsync a $ \a' ->
-    C.withAsync b $ \b' -> do
-      a'' <- C.wait a'
-      C.cancel b'
-      pure a''
-
 -- | run two actions concurrently, but wait and return on the left result.
 concurrentlyLeft :: MonadConc m => m a -> m b -> m a
 concurrentlyLeft left right =
@@ -142,15 +130,15 @@
     C.withAsync right $ \b ->
       C.wait b
 
--- | connect a committer and emitter action via spawning a queue, and wait for both to complete.
-withQC ::
+-- | connect a committer and emitter action via spawning a queue, and wait for the Committer action to complete.
+withQL ::
   (MonadConc m) =>
   Queue a ->
   (Queue a -> m (Box m a a, m ())) ->
   (Committer m a -> m l) ->
   (Emitter m a -> m r) ->
   m l
-withQC q spawner cio eio =
+withQL q spawner cio eio =
   C.bracket
     (spawner q)
     snd
@@ -160,15 +148,15 @@
           (eio (emitter box) `C.finally` seal)
     )
 
--- | connect a committer and emitter action via spawning a queue, and wait for both to complete.
-withQE ::
+-- | connect a committer and emitter action via spawning a queue, and wait for the Emitter action to complete.
+withQR ::
   (MonadConc m) =>
   Queue a ->
   (Queue a -> m (Box m a a, m ())) ->
   (Committer m a -> m l) ->
   (Emitter m a -> m r) ->
   m r
-withQE q spawner cio eio =
+withQR q spawner cio eio =
   C.bracket
     (spawner q)
     snd
@@ -178,33 +166,78 @@
           (eio (emitter box) `C.finally` seal)
     )
 
--- | create an unbounded queue, returning the emitter result
-queueC ::
+-- | connect a committer and emitter action via spawning a queue, and wait for both to complete.
+withQ ::
   (MonadConc m) =>
+  Queue a ->
+  (Queue a -> m (Box m a a, m ())) ->
   (Committer m a -> m l) ->
   (Emitter m a -> m r) ->
+  m (l,r)
+withQ q spawner cio eio =
+  C.bracket
+    (spawner q)
+    snd
+    ( \(box, seal) ->
+        concurrently
+          (cio (committer box) `C.finally` seal)
+          (eio (emitter box) `C.finally` seal)
+    )
+
+-- | Create an unbounded queue, returning the result from the Committer action.
+--
+-- >>> queueL New (\c -> glue c <$|> qList [1..3]) toListM
+queueL ::
+  (MonadConc m) =>
+  Queue a ->
+  (Committer m a -> m l) ->
+  (Emitter m a -> m r) ->
   m l
-queueC cm em = withQC Unbounded toBoxM cm em
+queueL q cm em = withQL q toBoxM cm em
 
--- | create an unbounded queue, returning the emitter result
-queueE ::
+-- | Create an unbounded queue, returning the result from the Emitter action.
+--
+-- >>> queueR New (\c -> glue c <$|> qList [1..3]) toListM
+-- [3]
+queueR ::
   (MonadConc m) =>
+  Queue a ->
   (Committer m a -> m l) ->
   (Emitter m a -> m r) ->
   m r
-queueE cm em = withQE Unbounded toBoxM cm em
+queueR q cm em = withQR q toBoxM cm em
 
+-- | Create an unbounded queue, returning both results.
+--
+-- >>> queue Unbounded (\c -> glue c <$|> qList [1..3]) toListM
+-- ((),[1,2,3])
+queue ::
+  (MonadConc m) =>
+  Queue a ->
+  (Committer m a -> m l) ->
+  (Emitter m a -> m r) ->
+  m (l, r)
+queue q cm em = withQ q toBoxM cm em
+
 -- | lift a box from STM
 liftB :: (MonadConc m) => Box (STM m) a b -> Box m a b
-liftB (Box c e) = Box (hoist atomically c) (hoist atomically e)
+liftB (Box c e) = Box (foist atomically c) (foist atomically e)
 
--- | turn a box action into a box continuation
-fromAction :: (MonadConc m) => (Box m a b -> m r) -> Cont m (Box m b a)
-fromAction baction = Cont $ fuseActions baction
+-- | Turn a box action into a box continuation
+fromAction :: (MonadConc m) => (Box m a b -> m r) -> CoBox m b a
+fromAction baction = Codensity $ fuseActions baction
 
--- | connect up two box actions via two queues
+-- | Connect up two box actions via two queues
 fuseActions :: (MonadConc m) => (Box m a b -> m r) -> (Box m b a -> m r') -> m r'
 fuseActions abm bam = do
   (Box ca ea, _) <- toBoxM Unbounded
   (Box cb eb, _) <- toBoxM Unbounded
   concurrentlyRight (abm (Box ca eb)) (bam (Box cb ea))
+
+-- | Hook a committer action to a queue, creating an emitter continuation.
+emitQ :: (MonadConc m) => Queue a -> (Committer m a -> m r) -> CoEmitter m a
+emitQ q cio = Codensity $ \eio -> queueR q cio eio
+
+-- | Hook a committer action to a queue, creating an emitter continuation.
+commitQ :: (MonadConc m) => Queue a -> (Emitter m a -> m r) -> CoCommitter m a
+commitQ q eio = Codensity $ \cio -> queueL q cio eio
diff --git a/src/Box/Time.hs b/src/Box/Time.hs
--- a/src/Box/Time.hs
+++ b/src/Box/Time.hs
@@ -7,41 +7,32 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 {-# OPTIONS_GHC -fno-warn-type-defaults #-}
 
--- | timing effects
+-- | Timing effects.
 module Box.Time
   ( sleep,
-    sleepUntil,
     Stamped (..),
     stampNow,
     stampE,
-    emitOn,
-    playback,
-    simulate,
+    emitIn,
+    replay,
   )
 where
 
-import Box.Cont
+import Box.Codensity
 import Box.Emitter
 import Control.Monad.Conc.Class as C
 import Control.Monad.IO.Class
 import Data.Fixed (Fixed (MkFixed))
 import Data.Time
 import Prelude
+import Control.Applicative
 
 -- $setup
 -- >>> :set -XOverloadedStrings
--- >>> :set -XGADTs
--- >>> :set -XFlexibleContexts
--- >>> import Data.Functor.Contravariant
 -- >>> import Box
--- >>> import Control.Applicative
--- >>> import Control.Monad.Conc.Class as C
--- >>> import Control.Lens
--- >>> import qualified Data.Sequence as Seq
--- >>> import Data.Text (pack, Text)
--- >>> import Data.Functor.Contravariant
+-- >>> import Prelude
 
--- | sleep for x seconds
+-- | Sleep for x seconds.
 sleep :: (MonadConc m) => Double -> m ()
 sleep x = C.threadDelay (floor $ x * 1e6)
 
@@ -61,12 +52,6 @@
       t1 = UTCTime (addDays days d0) (picosecondsToDiffTime $ floor (secs / 1.0e-12))
    in diffUTCTime t1 t0
 
--- | sleep until a certain time (in the future)
-sleepUntil :: UTCTime -> IO ()
-sleepUntil u = do
-  t0 <- getCurrentTime
-  sleep (fromNominalDiffTime $ diffUTCTime u t0)
-
 -- | A value with a UTCTime annotation.
 data Stamped a = Stamped
   { stamp :: !UTCTime,
@@ -80,39 +65,45 @@
   t <- liftIO getCurrentTime
   pure (utcToLocalTime utc t, a)
 
--- | adding a time stamp
+-- | Add the current time stamp.
+--
+-- @
+-- > process (toListM . stampE) (qList [1..3])
+-- [(2022-02-09 01:18:00.293883,1),(2022-02-09 01:18:00.293899,2),(2022-02-09 01:18:00.293903,3)]
+-- @
 stampE ::
   (MonadConc m, MonadIO m) =>
   Emitter m a ->
   Emitter m (LocalTime, a)
-stampE = mapE (fmap Just . stampNow)
+stampE = witherE (fmap Just . stampNow)
 
--- | wait until Stamped time before emitting
-emitOn ::
-  Emitter IO (LocalTime, a) ->
+-- | Wait s seconds before emitting
+emitIn ::
+  Emitter IO (Double, a) ->
   Emitter IO a
-emitOn =
-  mapE
-    ( \(l, a) -> do
-        sleepUntil (localTimeToUTC utc l)
+emitIn =
+  witherE
+    ( \(s, a) -> do
+        sleep s
         pure $ Just a
     )
 
--- | reset the emitter stamps to by in sync with the current time and adjust the speed
--- > let e1 = fromListE (zipWith (\x a -> Stamped (addUTCTime x t) a) [0..5] [0..5])
-playback :: Double -> Emitter IO (LocalTime, a) -> IO (Emitter IO (LocalTime, a))
-playback speed e = do
+-- | Convert emitter stamps to adjusted speed delays
+delay :: (Monad m, Alternative m) => Double -> Emitter m (LocalTime, b) -> m (Emitter m (Double, b))
+delay speed e = do
   r <- emit e
   case r of
     Nothing -> pure mempty
-    Just (l0, _) -> do
-      t0 <- getCurrentTime
-      let ua = diffLocalTime (utcToLocalTime utc t0) l0
-      let delta u = addLocalTime ua $ addLocalTime (toNominalDiffTime (fromNominalDiffTime (diffLocalTime u l0) * speed)) l0
-      pure (mapE (\(l, a) -> pure (Just (delta l, a))) e)
+    Just (t0, _) -> do
+      let delta u = fromNominalDiffTime $ diffLocalTime u t0 * toNominalDiffTime speed
+      pure (witherE (\(l, a) -> pure (Just (delta l, a))) e)
 
--- | simulate a delay from a (Stamped a) Emitter relative to the first timestamp
-simulate :: Double -> Emitter IO (LocalTime, a) -> Cont IO (Emitter IO a)
-simulate speed e = Cont $ \eaction -> do
-  e' <- playback speed e
-  eaction (emitOn e')
+-- | Replay a stamped emitter, adjusting the speed of the replay.
+--
+-- @
+-- > glueN 4 showStdout <$|> replay 1 (Emitter $ sleep 0.1 >> Just <$> stampNow ())
+-- @
+replay :: Double -> Emitter IO (LocalTime, a) -> CoEmitter IO a
+replay speed e = Codensity $ \eaction -> do
+  e' <- delay speed e
+  eaction (emitIn e')
