diff --git a/pipes.cabal b/pipes.cabal
--- a/pipes.cabal
+++ b/pipes.cabal
@@ -1,5 +1,5 @@
 Name: pipes
-Version: 4.0.1
+Version: 4.0.2
 Cabal-Version: >= 1.10
 Build-Type: Simple
 License: BSD3
@@ -77,7 +77,7 @@
 
     Build-Depends:
         base      >= 4       && < 5  ,
-        criterion >= 0.8     && < 0.9,
+        criterion >= 0.6.2.1 && < 0.9,
         mtl       >= 2.0.1.0 && < 2.2,
         pipes     >= 4.0.0   && < 4.1
 
@@ -106,7 +106,7 @@
 
     Build-Depends:
         base         >= 4       && < 5  ,
-        criterion    >= 0.8     && < 0.9,
+        criterion    >= 0.6.2.1 && < 0.9,
         deepseq                         ,
         mtl          >= 2.0.1.0 && < 2.2,
         pipes        >= 4.0.0   && < 4.1,
diff --git a/src/Pipes.hs b/src/Pipes.hs
--- a/src/Pipes.hs
+++ b/src/Pipes.hs
@@ -71,11 +71,11 @@
 
 import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))
 import Control.Monad (MonadPlus(mzero, mplus))
-import Control.Monad.IO.Class (MonadIO(liftIO)) -- transformers
-import Control.Monad.Trans.Class (MonadTrans(lift)) --transformers
+import Control.Monad.IO.Class (MonadIO(liftIO))
+import Control.Monad.Trans.Class (MonadTrans(lift))
 import Control.Monad.Trans.Error (ErrorT(runErrorT))
-import Control.Monad.Trans.Identity (IdentityT(runIdentityT)) --transformers
-import Control.Monad.Trans.Maybe (MaybeT(runMaybeT)) --transformers
+import Control.Monad.Trans.Identity (IdentityT(runIdentityT))
+import Control.Monad.Trans.Maybe (MaybeT(runMaybeT))
 import Data.Foldable (Foldable)
 import qualified Data.Foldable as F
 import Data.Monoid (Monoid(..))
@@ -470,10 +470,17 @@
 {-# INLINABLE next #-}
 
 -- | Convert a 'F.Foldable' to a 'Producer'
-each :: (Monad m, F.Foldable f) => f a -> Producer' a m ()
-each = F.mapM_ yield
+each :: (Monad m, Foldable f) => f a -> Producer' a m ()
+each = F.foldr (\a p -> yield a >> p) (return ())
 {-# INLINABLE each #-}
+{-  The above code is the same as:
 
+> each = Data.Foldable.mapM_ yield
+
+    ... except writing it directly in terms of `Data.Foldable.foldr` improves
+    build/foldr fusion
+-}
+
 -- | Convert an 'Enumerable' to a 'Producer'
 every :: (Monad m, Enumerable t) => t m a -> Producer' a m ()
 every it = discard >\\ enumerate (toListT it)
@@ -506,5 +513,5 @@
 #endif
     "Data.Foldable" re-exports 'Foldable' (the class name only)
 
-    "Data.Void" re-exports 'Void'.
+    "Data.Void" re-exports 'Void'
 -}
diff --git a/src/Pipes/Core.hs b/src/Pipes/Core.hs
--- a/src/Pipes/Core.hs
+++ b/src/Pipes/Core.hs
@@ -81,9 +81,13 @@
     , (<\\)
     , (//<)
     , (<<+)
+
+    -- * Re-exports
+    , module Data.Void
     ) where
 
-import Data.Void (Void, absurd)
+import Data.Void (Void)
+import qualified Data.Void as V
 import Pipes.Internal (Proxy(..))
 
 {- $proxy
@@ -121,8 +125,8 @@
 runEffect = go
   where
     go p = case p of
-        Request v _ -> absurd v
-        Respond v _ -> absurd v
+        Request v _ -> V.absurd v
+        Respond v _ -> V.absurd v
         M       m   -> m >>= go
         Pure    r   -> return r
 {-# INLINABLE runEffect #-}
@@ -842,3 +846,7 @@
   ; "f >\\ request x" forall f x . f >\\ request x = f x
 
   #-}
+
+{- $reexports
+    @Data.Void@ re-exports the 'Void' type
+-}
diff --git a/src/Pipes/Prelude.hs b/src/Pipes/Prelude.hs
--- a/src/Pipes/Prelude.hs
+++ b/src/Pipes/Prelude.hs
@@ -11,8 +11,9 @@
     dependency on the @text@ package.
 
     Also, 'stdinLn' and 'stdoutLn' remove and add newlines, respectively.  This
-    behavior is intended to simplify examples.  The upcoming 'ByteString' and
-    'Text' utilities for @pipes@ will preserve newlines.
+    behavior is intended to simplify examples.  The corresponding @stdin@ and
+    @stdout@ utilities from @pipes-bytestring@ and @pipes-text@ preserve
+    newlines.
 -}
 
 {-# LANGUAGE RankNTypes, CPP #-}
@@ -98,14 +99,14 @@
 import Foreign.C.Error (Errno(Errno), ePIPE)
 import qualified GHC.IO.Exception as G
 import Pipes
+import Pipes.Core
 import Pipes.Internal
 import qualified System.IO as IO
-import qualified Prelude
 #ifndef haskell98
 import Control.Monad.Trans.State.Strict (get, put)
-import Pipes.Core
 import Pipes.Lift (evalStateP)
 #endif
+import qualified Prelude
 import Prelude hiding (
       all
     , and
@@ -548,9 +549,9 @@
 head :: (Monad m) => Producer a m () -> m (Maybe a)
 head p = do
     x <- next p
-    case x of
-        Left   _     -> return Nothing
-        Right (a, _) -> return (Just a)
+    return $ case x of
+        Left   _     -> Nothing
+        Right (a, _) -> Just a
 {-# INLINABLE head #-}
 
 -- | Index into a 'Producer'
diff --git a/src/Pipes/Tutorial.hs b/src/Pipes/Tutorial.hs
--- a/src/Pipes/Tutorial.hs
+++ b/src/Pipes/Tutorial.hs
@@ -80,6 +80,9 @@
 
     -- * Appendix: Types
     -- $types
+
+    -- * Appendix: Time Complexity
+    -- $timecomplexity
     ) where
 
 import Control.Category
@@ -1358,27 +1361,46 @@
 
     However, polymorphic type synonyms cause problems in many other cases:
 
-    * They induce higher-rank types and require you to enable the @RankNTypes@
-      extension to use them in your own type signatures.
-
-    * They give the wrong behavior when used in the negative position of a
-      function like this:
+    * They usually give the wrong behavior when used as the argument of a
+      function (known as the \"negative\" or \"contravariant\" position) like
+      this:
 
 > f :: Producer' a m r -> ...  -- Wrong
 >
 > f :: Producer  a m r -> ...  -- Right
 
-    * You can't use them within other types without the @ImpredicativeTypes@
-      extension:
+      The former function only accepts polymorphic 'Producer's as arguments.
+      The latter function accepts both polymorphic and concrete 'Producer's,
+      which is probably what you want.
 
-> io :: IO (Producer' a m r)  -- Type error
+    * Even when you desire a polymorphic argument, this induces a higher-ranked
+      type, because it translates to a @forall@ which you cannot factor out to
+      the top-level to simplify the type signature:
 
-    * You can't partially apply them:
+> f :: (forall x' x y' . Proxy x' x y' m r) -> ...
 
+      These kinds of type signatures require the @RankNTypes@ extension.
+
+    * Even when you have polymorphic type synonyms as the result of a function
+      (i.e.  the \"positive\" or \"covariant\" position), recent versions of
+      @ghc@ such still require the @RankNTypes@ extension.  For example, the
+      'Pipes.Prelude.fromHandle' function from "Pipes.Prelude" requires
+      @RankNTypes@ to compile correctly on @ghc-7.6.3@:
+
+> fromHandle :: (MonadIO m) => Handle -> Producer' String m ()
+
+    * You can't use polymorphic type synonyms inside other type constructors
+      without the @ImpredicativeTypes@ extension:
+
+> io :: IO (Producer' a m r)  -- Type error without ImpredicativeTypes
+
+    * You can't partially apply polymorphic type synonyms:
+
 > stack :: MaybeT (Producer' a m) r  -- Type error
 
     In these scenarios you should fall back on the concrete type synonyms, which
-    are better behaved.
+    are better behaved.  If concrete type synonyms are unsatisfactory, then ask
+    @ghc@ to infer the most general type signature and use that.
 
     For the purposes of debugging type errors you can just remember that:
 
@@ -1438,4 +1460,49 @@
 > type Server'       b' b m r = forall x' x      . Proxy x' x b' b m r
 > type Client'  a' a      m r = forall      y' y . Proxy a' a y' y m r
 
+-}
+
+{- $timecomplexity
+    There are three functions that give quadratic time complexity when used in
+    within @pipes@:
+
+    * 'sequence'
+
+    * 'replicateM'
+
+    * 'mapM'
+
+    For example, the time complexity of this code segment scales quadratically
+    with `n`:
+
+> import Control.Monad (replicateM)
+> import Pipes
+>
+> quadratic :: Int -> Consumer a m [a]
+> quadratic n = replicateM n await
+
+    These three functions are generally bad practice to use, because all three
+    of them correspond to \"ListT done wrong\", building a list in memory
+    instead of streaming results.
+
+    However, sometimes situations arise where one deliberately intends to build
+    a list in memory.  The solution is to use the \"codensity transformation\"
+    to transform the code to run with linear time complexity.  This involves:
+
+    * wrapping the code in the @Codensity@ monad transformer (from
+      @Control.Monad.Codensity@ module of the @kan-extensions@ package) using
+      'lift'
+
+    * applying 'sequence' \/ 'replicateM' \/ 'mapM'
+
+    * unwrapping the code using @lowerCodensity@
+
+    To illustrate this, we'd transform the above example to:
+
+> import Control.Monad.Codensity (lowerCodensity)
+> 
+> linear :: (Monad m) => Int -> Consumer a m [a]
+> linear n = lowerCodensity $ replicateM n $ lift await
+
+    This will produce the exact same result, but in linear time.
 -}
