diff --git a/quiver.cabal b/quiver.cabal
--- a/quiver.cabal
+++ b/quiver.cabal
@@ -1,5 +1,5 @@
 name:           quiver
-version:        0.0.0.3
+version:        0.0.0.4
 synopsis:       Quiver finite stream processing library
 homepage:       https://github.com/zadarnowski/quiver
 category:       Control
@@ -35,7 +35,7 @@
 source-repository this
   type:         git
   location:     https://github.com/zadarnowski/quiver.git
-  tag:          0.0.0.3
+  tag:          0.0.0.4
 
 library
   hs-source-dirs:   src
diff --git a/src/Control/Quiver.lhs b/src/Control/Quiver.lhs
--- a/src/Control/Quiver.lhs
+++ b/src/Control/Quiver.lhs
@@ -19,7 +19,9 @@
 >   -- Defined below:
 >   fetch, fetch',
 >   emit, emit', emit_,
->   liftP,
+>   qlift,
+>   qpure, qpure_, qid,
+>   qconcat, qconcat_,
 >   runEffect,
 >   (>>->), (>->>),
 > ) where
@@ -33,7 +35,7 @@
 > --   next input value received, or @Nothing@ if the upstream
 > --   processor has been depleted.
 
-> fetch :: a' -> P a' a b' b f (Maybe a)
+> fetch :: a' -> P a' a b b' f (Maybe a)
 > fetch x = consume x (deliver . Just) (deliver Nothing)
 
 > -- | @fetch' x q@ represents a singleton stream processor that
@@ -41,7 +43,7 @@
 > --   input value received, or, if the upstream processor has
 > --   been depleted, continues with the decoupled processor @q@.
 
-> fetch' :: a' -> Producer b' b f a -> P a' a b' b f a
+> fetch' :: a' -> Producer b b' f a -> P a' a b b' f a
 > fetch' x q = consume x deliver q
 
 > -- | @emit y@ represents a singleton stream processor that
@@ -49,7 +51,7 @@
 > --   response received from the downstream processor, or
 > --   @Nothing@ if the downstream processor has been decoupled.
 
-> emit :: b -> P a' a b' b f (Maybe b')
+> emit :: b -> P a' a b b' f (Maybe b')
 > emit y = produce y (deliver . Just) (deliver Nothing)
 
 > -- | @emit' y q@ represents a singleton stream processor that
@@ -58,21 +60,71 @@
 > --   if the downstream processor has been decoupled, continues
 > --   with the depleted processor @q@.
 
-> emit' :: b -> Consumer a' a f b' -> P a' a b' b f b'
+> emit' :: b -> Consumer a' a f b' -> P a' a b b' f b'
 > emit' y q = produce y deliver q
 
 > -- | @emit' y q@ represents a singleton stream processor that
 > --   produces a single output value @y@, ignoring any response
 > --   received from the downstream processor.
 
-> emit_ :: b -> P a' a b' b f ()
+> emit_ :: b -> P a' a b b' f ()
 > emit_ y = produce y (deliver . const ()) (deliver ())
 
-> -- | @liftP@ lifts the value of a base functor into a stream processor.
+> -- | @qlift@ lifts the value of a base functor into a stream processor.
 
-> liftP :: Functor f => f r -> P a' a b' b f r
-> liftP = enclose . fmap deliver
+> qlift :: Functor f => f r -> P a' a b b' f r
+> qlift = enclose . fmap deliver
 
+> -- | @qpure g f z@ produces an infinite consumer/producer that
+> --   uses a pure function @f@ to convert every input value into
+> --   an output, and @f@ to convert each downstream response value
+> --   into an upstream request; the initial request is obtained
+> --   by applying @g@ to the initial response value @z@.
+
+> qpure :: (b' -> a') -> (a -> b) -> b' -> P a' a b b' f ()
+> qpure g f = cloop
+>  where
+>   cloop y = let y' = g y in consume y' ploop (deliver ())
+>   ploop x = let x' = f x in produce x' cloop (deliver ())
+
+> -- | @qpure_ f@ produces an infinite consumer/producer that
+> --   uses a pure function @f@ to convert every input value into
+> --   an output; equivalent to @qpure id f (const ())@.
+
+> qpure_ :: (a -> b) -> P () a b b' f ()
+> qpure_ f = cloop
+>  where
+>   cloop = consume () ploop (deliver ())
+>   ploop x = produce (f x) (const cloop) (deliver ())
+
+> -- | A pull-based identity processor, equivalent to 'qpure id id'.
+
+> qid :: b -> P b a a b f ()
+> qid = cloop
+>  where
+>   cloop z = consume z ploop (deliver ())
+>   ploop x = produce x cloop (deliver ())
+
+> -- | A pull-based list flattening processor, delivering the list
+> --   of inputs that could not be produced and a list of responses
+> --   that could not be consumed.
+
+> qconcat :: [b] -> P [b] [a] a b f ([a], [b])
+> qconcat = cloop
+>  where
+>   cloop ys = consume ys (ploop []) (deliver ([], []))
+>   ploop ys (x:xs) = produce x (\y -> ploop (y:ys) xs) (deliver (xs, reverse ys))
+>   ploop ys [] = cloop (reverse ys)
+
+> -- | A pull-based list flattening processor without requests.
+
+> qconcat_ :: P () [a] a b f [a]
+> qconcat_ = cloop
+>  where
+>   cloop = consume () ploop (deliver [])
+>   ploop (x:xs) = produce x (const $ ploop xs) (deliver xs)
+>   ploop [] = cloop
+
 > -- | Evaluates an /effect/, i.e., a processor that is both detached
 > --   and depleted and hence neither consumes nor produces any input,
 > --   returning its delivered value. The base functor must be a monad.
@@ -92,7 +144,7 @@
 > --   represents a non-commutative monad, any effects of @p2@ will be
 > --   observed before those of @p1@.
 
-> (>>->) :: Functor f => P a' a b' b f r1 -> P b' b c' c f r2 -> P a' a c' c f (r1, r2)
+> (>>->) :: Functor f => P a' a b b' f r1 -> P b' b c c' f r2 -> P a' a c c' f (r1, r2)
 > (Consume x1 k1 q1) >>-> p2 = consume x1 ((>>-> p2) . k1) (q1 >>-> p2)
 > (Produce y1 k1 q1) >>-> p2 = loop p2
 >  where
@@ -114,7 +166,7 @@
 > --   represents a non-commutative monad, any effects of @p1@ will be
 > --   observed before those of @p2@.
 
-> (>->>) :: Functor f => P a' a b' b f r1 -> P b' b c' c f r2 -> P a' a c' c f (r1, r2)
+> (>->>) :: Functor f => P a' a b b' f r1 -> P b' b c c' f r2 -> P a' a c c' f (r1, r2)
 > p1 >->> (Consume x2 k2 q2) = loop p1
 >  where
 >   loop  (Consume x1 k1 q1) = consume x1 (loop . k1) (loop' q1)
diff --git a/src/Control/Quiver/Internal.lhs b/src/Control/Quiver/Internal.lhs
--- a/src/Control/Quiver/Internal.lhs
+++ b/src/Control/Quiver/Internal.lhs
@@ -37,7 +37,7 @@
   Data Types
   ==========
 
-> -- | The main Quiver /stream processor/ type @P a' a b' b f r@,
+> -- | The main Quiver /stream processor/ type @P a' a b b' f r@,
 > --   representing a producer/consumer structure with /bidirectional/,
 > --   /bounded/ communication on both the upstream (consumer) and
 > --   downstream (producer) channel. The six type parameters have
@@ -48,8 +48,11 @@
 > --     next element of the input stream.
 > --
 > --   * @a@ is the type of the actual information being consumed
-> --     by this stream processor (i.e., its input stream.)
+> --     by this stream processor (i.e., elements of its input stream.)
 > --
+> --   * @b@ is the type of the actual information being produced
+> --     by this stream processor (i.e., elements of its output stream.)
+> --
 > --   * @b'@ is the type of the /response/ values received from
 > --     the downstream partner for each elemnet of the output
 > --     stream produced by this stream processor.
@@ -69,7 +72,7 @@
 > --   only time will tell whether this generalisation has useful
 > --   applications in the real world.
 
-> data P a' a b' b f r =
+> data P a' a b b' f r =
 
 >   -- | @Consume x k q@ represents a /consumer step/, in which
 >   --   the request @x@ is sent upstream and the returned input
@@ -78,7 +81,7 @@
 >   --   delivered its ultimate result, hence reaching the end
 >   --   of processing), to the /decoupled continuation/ @q@.
 
->   Consume a' (a -> P a' a b' b f r) (Producer b' b f r) |
+>   Consume a' (a -> P a' a b b' f r) (Producer b b' f r) |
 
 >   -- | @Produce y k q@ represent a /producer step/, in which
 >   --   the output value @y@ is sent downstream, and the returned
@@ -87,12 +90,12 @@
 >   --   (i.e., delivered its ultimate result, hence reaching the end
 >   --   of processing), to the /depleted continuation/ @q@.
 
->   Produce b  (b' -> P a' a b' b f r) (Consumer a' a f r) |
+>   Produce b  (b' -> P a' a b b' f r) (Consumer a' a f r) |
 
 >   -- | @Enclose@ allows for selective application of the base
 >   --   functor @f@ the the remainder of the computation.
 
->   Enclose (f (P a' a b' b f r)) |
+>   Enclose (f (P a' a b b' f r)) |
 
 >   -- | @Deliver r@ completes processing of information, delivering
 >   --   its ultimate result @r@.
@@ -102,36 +105,36 @@
 > -- | A Quiver /producer/, represented by a stream processor
 > --   with unspecified input types.
 
-> type Producer b' b f r = forall a' a . P a' a b' b f r
+> type Producer b b' f r = forall a' a . P a' a b b' f r
 
 > -- | A Quiver /consumer/, represented by a stream processor
 > --   with unspecified output types.
 
-> type Consumer a' a f r = forall b' b . P a' a b' b f r
+> type Consumer a' a f r = forall b b' . P a' a b b' f r
 
 > -- | A Quiver /effect/, represented by a stream processor
 > --   with unspecified input and output types.
 
-> type Effect f r = forall a' a b' b . P a' a b' b f r
+> type Effect f r = forall a' a b b' . P a' a b b' f r
 
 
   Instances
   =========
 
-> instance Functor f => Functor (P a' a b' b f) where
+> instance Functor f => Functor (P a' a b b' f) where
 >   fmap ff (Consume x k q) = Consume x (fmap ff . k) (fmap ff q)
 >   fmap ff (Produce y k q) = Produce y (fmap ff . k) (fmap ff q)
 >   fmap ff (Enclose f)     = Enclose (fmap (fmap ff) f)
 >   fmap ff (Deliver r)     = Deliver (ff r)
 
-> instance Applicative f => Applicative (P a' a b' b f) where
+> instance Applicative f => Applicative (P a' a b b' f) where
 >   pure = Deliver
 >   (Consume x k q) <*> p = Consume x ((<*> p) . k) (q <*> decouple p)
 >   (Produce y k q) <*> p = Produce y ((<*> p) . k) (q <*> deplete p)
 >   (Enclose f)     <*> p = Enclose (fmap (<*> p) f)
 >   (Deliver r)     <*> p = fmap r p
 
-> instance Monad f => Monad (P a' a b' b f) where
+> instance Monad f => Monad (P a' a b b' f) where
 >   (Consume x k q) >>= kk = Consume x ((>>= kk) . k) (q >>= decouple . kk)
 >   (Produce y k q) >>= kk = Produce y ((>>= kk) . k) (q >>= deplete . kk)
 >   (Enclose f)     >>= kk = Enclose (fmap (>>= kk) f)
@@ -148,7 +151,7 @@
 > --   delivered its ultimate result, hence reaching the end
 > --   of processing), to the /decoupled continuation/ @q@.
 
-> consume :: a' -> (a -> P a' a b' b f r) -> Producer b' b f r -> P a' a b' b f r
+> consume :: a' -> (a -> P a' a b b' f r) -> Producer b b' f r -> P a' a b b' f r
 > consume = Consume
 
 > -- | @produce y k q@ represent a /producer step/, in which
@@ -158,19 +161,19 @@
 > --   (i.e., delivered its ultimate result, hence reaching the end
 > --   of processing), to the /depleted continuation/ @q@.
 
-> produce :: b  -> (b' -> P a' a b' b f r) -> Consumer a' a f r -> P a' a b' b f r
+> produce :: b  -> (b' -> P a' a b b' f r) -> Consumer a' a f r -> P a' a b b' f r
 > produce = Produce
 
 > -- | @enclose@ allows for selective application of the base
 > --   functor @f@ the the remainder of the computation.
 
-> enclose :: f (P a' a b' b f r) -> P a' a b' b f r
+> enclose :: f (P a' a b b' f r) -> P a' a b b' f r
 > enclose = Enclose
 
 > -- | @deliver r@ completes processing of information, delivering
 > --   its ultimate result @r@.
 
-> deliver :: r -> P a' a b' b f r
+> deliver :: r -> P a' a b b' f r
 > deliver = Deliver
 
 
@@ -182,7 +185,7 @@
 > --   effectively converting @p@ into a producer processor that no longer
 > --   expects to receive any input.
 
-> decouple :: Functor f => P a' a b' b f r -> Producer b' b f r
+> decouple :: Functor f => P a' a b b' f r -> Producer b b' f r
 > decouple (Consume _ _ q) = q
 > decouple (Produce y k q) = Produce y (decouple . k) (decouple q)
 > decouple (Enclose f) = Enclose (fmap decouple f)
@@ -193,7 +196,7 @@
 > --   effectively converting @p@ into a consumer processor that will never
 > --   produce any more output.
 
-> deplete :: Functor f => P a' a b' b f r -> Consumer a' a f r
+> deplete :: Functor f => P a' a b b' f r -> Consumer a' a f r
 > deplete (Consume x k q) = Consume x (deplete . k) (deplete q)
 > deplete (Produce _ _ q) = q
 > deplete (Enclose f) = Enclose (fmap deplete f)
