diff --git a/pipe-enumerator.cabal b/pipe-enumerator.cabal
--- a/pipe-enumerator.cabal
+++ b/pipe-enumerator.cabal
@@ -1,6 +1,6 @@
 name:           pipe-enumerator
-version:        0.1.0.0
-synopsis:       Pipes/iteratees bridge library
+version:        0.2.0.0
+synopsis:       A bidirectional bridge between pipes and iteratees
 homepage:       https://github.com/zadarnowski/pipe-enumerator
 category:       Control, Pipes
 stability:      alpha
@@ -12,8 +12,16 @@
 
 description:
 
-    This library provides a bridge between pipe-based and iteratee-based programs.
+    This library defines a set of functions that convert between
+    the "Pipes" and "Data.Enumerator" paradigms. The conversion
+    is bidirectional: an appropriately-typed pipe can be converted
+    into an 'E.Iteratee' and back into a pipe. In addition, a pipe
+    can be fed into an iteratee (or, more specifically, 'E.Step'),
+    resulting in an 'E.Enumerator'.
 
+    The library has been designed specifically for use with "Snap",
+    but I'm sure that many other interesting uses of it exist.
+
 cabal-version:  >= 1.18
 build-type:     Simple
 license:        BSD3
@@ -26,7 +34,7 @@
 source-repository this
   type:         git
   location:     https://github.com/zadarnowski/pipe-enumerator.git
-  tag:          0.1.0.0
+  tag:          0.2.0.0
 
 library
   hs-source-dirs:   src
diff --git a/src/Pipes/Enumerator.lhs b/src/Pipes/Enumerator.lhs
--- a/src/Pipes/Enumerator.lhs
+++ b/src/Pipes/Enumerator.lhs
@@ -1,61 +1,122 @@
+> -- | Module:    Pipes.Enumerator
+> -- Description: A bidirectional bridge between pipes and iteratees
+> -- Copyright:   © 2015 Patryk Zadarnowski <pat@jantar.org>
+> -- License:     BSD3
+> -- Maintainer:  pat@jantar.org
+> -- Stability:   experimental
+> -- Portability: portable
+> --
+> -- This module defines a set of functions that convert between
+> -- the "Pipes" and "Data.Enumerator" paradigms. The conversion
+> -- is bidirectional: an appropriately-typed pipe can be converted
+> -- into an 'E.Iteratee' and back into a pipe. In addition, a pipe
+> -- can be fed into an iteratee (or, more specifically, 'E.Step'),
+> -- resulting in an 'E.Enumerator'.
+> --
+> -- The library has been designed specifically for use with "Snap",
+> -- but I'm sure that many other interesting uses of it exist.
+
 > module Pipes.Enumerator (
+>   fromStream, toStream, toSingletonStream,
+>   iterateeToPipe, stepToPipe,
+>   pipeToIteratee,
 >   pipeToEnumerator
 > ) where
 
+> import Control.Exception (SomeException)
 > import Control.Monad.Trans.Class
 > import qualified Pipes.Internal as P
 > import qualified Data.Enumerator as E
 
+
   Data Type Reference
   ===================
 
-    data Proxy a' a b' b m r
-        = Request a' (a  -> Proxy a' a b' b m r )
-        | Respond b  (b' -> Proxy a' a b' b m r )
-        | M          (m    (Proxy a' a b' b m r))
-        | Pure    r
+  Definitions of the relevant Pipe and Iteratee types, for reference:
 
-    type Pipe a b = Proxy () a () b
+    data P.Proxy a' a b' b m r
+        = P.Request a' (a  -> P.Proxy a' a b' b m r )
+        | P.Respond b  (b' -> P.Proxy a' a b' b m r )
+        | P.M          (m    (P.Proxy a' a b' b m r))
+        | P.Pure    r
 
-    type Enumerator a m b = Step a m b -> Iteratee a m b
+    type P.Pipe a b = P.Proxy () a () b
 
-    data Step a m b =
-        Continue (Stream a -> Iteratee a m b)
-      | Yield b (Stream a)
-      | Error SomeException
+    type E.Enumerator a m b = E.Step a m b -> E.Iteratee a m b
 
-    newtype Iteratee a m b = Iteratee { runIteratee :: m (Step a m b) }
+    data E.Step a m b =
+        E.Continue (E.Stream a -> E.Iteratee a m b)
+      | E.Yield b (E.Stream a)
+      | E.Error SomeException
 
-    data Stream a =
-        Chunks [a]
-      | EOF
+    newtype E.Iteratee a m b = E.Iteratee { E.runIteratee :: m (E.Step a m b) }
 
-  Connect a pipe to an iteratee, effectively converting it into an enumerator,
-  generalised slightly to allow distinct input and output types. The chunks of
-  the input stream are fed into the pipe one element at the time, and the pipe's
-  output is fed to the iteratee one element per chunk. Once the input reaches
-  @E.EOF@, the pipe is fed an infinite stream of @Nothing@ until it ends with
-  a pure value. In effect, both the pipe and the iteratee are always consumed
-  fully, with the resulting enumerator returning the results of both.
+    data E.Stream a =
+        E.Chunks [a]
+      | E.EOF
 
+> -- | Converts a 'E.Stream' to an optional list.
+> fromStream :: E.Stream a -> Maybe [a]
+> fromStream (E.Chunks cs) = Just cs
+> fromStream (E.EOF)       = Nothing
+
+> -- | Converts an optional list to a 'E.Stream'.
+> toStream :: Maybe [a] -> E.Stream a
+> toStream (Just cs) = E.Chunks cs
+> toStream (Nothing) = E.EOF
+
+> -- | Converts an optional value to a singleton 'E.Stream'.
+> toSingletonStream :: Maybe a -> E.Stream a
+> toSingletonStream (Just c)  = E.Chunks [c]
+> toSingletonStream (Nothing) = E.EOF
+
+> -- | Converts an 'E.Iteratee' into a 'P.Consumer'.
+> iterateeToPipe :: Monad m => E.Iteratee a m r -> P.Proxy () (Maybe a) b' b m (Either SomeException (r, Maybe [a]))
+> iterateeToPipe = P.M . fmap stepToPipe . E.runIteratee
+
+> -- | Converts a 'E.Step' into a 'P.Consumer'.
+> stepToPipe :: Monad m => E.Step a m r -> P.Proxy () (Maybe a) b' b m (Either SomeException (r, Maybe [a]))
+> stepToPipe (E.Continue ik) = P.Request () (P.M . fmap stepToPipe . E.runIteratee . ik . toSingletonStream)
+> stepToPipe (E.Yield r xs)  = return (Right (r, fromStream xs))
+> stepToPipe (E.Error e)     = return (Left e)
+
+> -- | Converts a 'P.Pipe' into an 'E.Iteratee'.
+> --   Any output of the pipe is quietly discarded.
+> pipeToIteratee :: Monad m => P.Proxy a' (Maybe a) () b m r -> E.Iteratee a m r
+> pipeToIteratee = convert1 []
+>  where
+>   convert1 cs (P.Request _ pk)    = convert2 pk cs
+>   convert1 cs (P.Respond _ pk)    = convert1 cs (pk ())
+>   convert1 cs (P.M m)             = lift m >>= convert1 cs
+>   convert1 cs (P.Pure r)          = E.yield r (E.Chunks cs)
+>   convert2 pk (c:cs)              = convert1 cs (pk (Just c))
+>   convert2 pk []                  = E.continue (convert3 pk)
+>   convert3 pk (E.Chunks cs)       = convert2 pk cs
+>   convert3 pk (E.EOF)             = convert4 pk
+>   convert4 pk                     = convert5 (pk Nothing)
+>   convert5 (P.Request _ pk)       = convert4 pk
+>   convert5 (P.Respond _ pk)       = convert5 (pk ())
+>   convert5 (P.M m)                = lift m >>= convert5
+>   convert5 (P.Pure r)             = E.yield r E.EOF
+
+> -- | Feed the output of a 'P.Pipe' to a 'E.Step', effectively converting it into
+> --   an 'E.Enumerator',  generalised slightly to allow distinct input and output
+> --   types.  The chunks of the input stream are fed into the pipe one element at
+> --   the time, and the pipe's  output is fed to the iteratee one element per chunk.
+> --   Once the input reaches 'E.EOF', the pipe is fed an infinite stream of 'Nothing'
+> --   until it ends with  a pure value. In effect, both the pipe and the iteratee are
+> --   always consumed fully, with the resulting enumerator returning the results of both.
 > pipeToEnumerator :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
 > pipeToEnumerator = advanceIteratee []
 >  where
 
-    State Machine 1
-    ===============
-
-    Advance a single iteratee step, keeping track of any input @cs@ that has been
-    supplied by the upstream enumerator but not yet used. If the target iteratee blocks,
-    we will supply it with input obtained by advancing source pipe; otherwise, we'll
-    simply drain the pipe to completion, in order to retrieve the remainder of the
-    upstream input and the pipe's return value:
-
 >   advanceIteratee :: Monad m => [a] -> P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
 >   advanceIteratee cs p (E.Continue ik)      = advancePipe cs ik p
 >   advanceIteratee cs p s                    = drainPipe cs s p
 
-    Advance a pipe, feeding its response into a blocked iteratee @ik@.
+>   advanceIteratee' :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
+>   advanceIteratee' p (E.Continue ik)        = advancePipe' ik p
+>   advanceIteratee' p s                      = drainPipe' s p
 
 >   advancePipe :: Monad m => [a] -> (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
 >   advancePipe cs ik (P.Request _ pk)        = advancePipeWithChunks ik pk cs
@@ -63,57 +124,45 @@
 >   advancePipe cs ik (P.M m)                 = lift m >>= advancePipe cs ik
 >   advancePipe cs ik (P.Pure q)              = ik (E.EOF) E.>>== finish q (E.Chunks cs)
 
->   advancePipeWithChunks :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)
->   advancePipeWithChunks ik pk (c:cs)        = advancePipe cs ik (pk (Just c))
->   advancePipeWithChunks ik pk []            = E.continue (advancePipeWithStream ik pk)
+>   advancePipe' :: Monad m => (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
+>   advancePipe' ik (P.Request _ pk)          = advancePipeWithEOF ik pk
+>   advancePipe' ik (P.Respond x pk)          = ik (E.Chunks [x]) E.>>== advanceIteratee' (pk ())
+>   advancePipe' ik (P.M m)                   = lift m >>= advancePipe' ik
+>   advancePipe' ik (P.Pure q)                = ik (E.EOF) E.>>== finish q (E.EOF)
 
 >   advancePipeWithStream :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)
 >   advancePipeWithStream ik pk (E.Chunks cs) = advancePipeWithChunks ik pk cs
 >   advancePipeWithStream ik pk (E.EOF)       = advancePipeWithEOF ik pk
 
+>   advancePipeWithChunks :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)
+>   advancePipeWithChunks ik pk (c:cs)        = advancePipe cs ik (pk (Just c))
+>   advancePipeWithChunks ik pk []            = E.continue (advancePipeWithStream ik pk)
+
+>   advancePipeWithEOF :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)
+>   advancePipeWithEOF ik pk                  = advancePipe' ik (pk Nothing)
+
 >   drainPipe :: Monad m => [a] -> E.Step b m r -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
 >   drainPipe cs s (P.Request _ pk)           = drainPipeWithChunks s pk cs
 >   drainPipe cs s (P.Respond _ pk)           = drainPipe cs s (pk ())
 >   drainPipe cs s (P.M m)                    = lift m >>= drainPipe cs s
 >   drainPipe cs s (P.Pure q)                 = finish q (E.Chunks cs) s
 
->   drainPipeWithChunks :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)
->   drainPipeWithChunks s pk (c:cs)           = drainPipe cs s (pk (Just c))
->   drainPipeWithChunks s pk []               = E.continue (drainPipeWithStream s pk)
-
->   drainPipeWithStream :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)
->   drainPipeWithStream s pk (E.Chunks cs)    = drainPipeWithChunks s pk cs
->   drainPipeWithStream s pk (E.EOF)          = drainPipeWithEOF s pk
-
-
-    State Machine 2
-    ===============
-
->   advanceIteratee' :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
->   advanceIteratee' p (E.Continue ik)        = advancePipe' ik p
->   advanceIteratee' p s                      = drainPipe' s p
-
->   advancePipe' :: Monad m => (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
->   advancePipe' ik (P.Request _ pk)          = advancePipeWithEOF ik pk
->   advancePipe' ik (P.Respond x pk)          = ik (E.Chunks [x]) E.>>== advanceIteratee' (pk ())
->   advancePipe' ik (P.M m)                   = lift m >>= advancePipe' ik
->   advancePipe' ik (P.Pure q)                = ik (E.EOF) E.>>== finish q (E.EOF)
-
->   advancePipeWithEOF :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)
->   advancePipeWithEOF ik pk                  = advancePipe' ik (pk Nothing)
-
 >   drainPipe' :: Monad m => E.Step b m r -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
 >   drainPipe' s (P.Request _ pk)             = drainPipeWithEOF s pk
 >   drainPipe' s (P.Respond _ pk)             = drainPipe' s (pk ())
 >   drainPipe' s (P.M m)                      = lift m >>= drainPipe' s
 >   drainPipe' s (P.Pure q)                   = finish q (E.EOF) s
 
->   drainPipeWithEOF :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)
->   drainPipeWithEOF s pk                     = drainPipe' s (pk Nothing)
+>   drainPipeWithStream :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)
+>   drainPipeWithStream s pk (E.Chunks cs)    = drainPipeWithChunks s pk cs
+>   drainPipeWithStream s pk (E.EOF)          = drainPipeWithEOF s pk
 
+>   drainPipeWithChunks :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)
+>   drainPipeWithChunks s pk (c:cs)           = drainPipe cs s (pk (Just c))
+>   drainPipeWithChunks s pk []               = E.continue (drainPipeWithStream s pk)
 
-    Final Step
-    ==========
+>   drainPipeWithEOF :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)
+>   drainPipeWithEOF s pk                     = drainPipe' s (pk Nothing)
 
 >   finish :: Monad m => q -> E.Stream a -> E.Step b m r -> E.Iteratee a m (q, r)
 >   finish q xs (E.Yield r _)                 = E.yield (q, r) xs
