diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,11 +3,65 @@
 Extra functionality for the [Process library](http://hackage.haskell.org/package/process).
 
  * Read process input and output as ByteStrings or Text, or write your own ProcessOutput instance.
- * Lazy process input and output.
+ * Lazy process input and output (i.e. read output from processes that run forever.)
  * ProcessMaker class for more flexibility in the process creation API.
 
 # Contributing
 
 This project is available on [GitHub](https://github.com/seereason/process-extras). You may contribute changes there.
 
-Please report bugs and feature requests using the [GitHub issue tracker](https://github.com/ddssff/process-extras/issues).
+Please report bugs and feature requests using the [GitHub issue tracker](https://github.com/seereason/process-extras/issues).
+
+# Examples:
+
+The output type of the raw system process functions is ByteString.
+Instances of ListLikeProcessIO are provided to read as type String,
+Text, Lazy Text, ByteString, or Lazy ByteString.  Select by casting
+the result, or by specifying the module containing the specialized
+function:
+
+    > :m +System.Process.ListLike Data.ByteString Data.Text.Lazy
+    > readCreateProcessWithExitCode (shell "echo 'λ'") mempty :: IO (ExitCode, ByteString, ByteString)
+    (ExitSuccess,"\206\187\n","")
+    > readCreateProcessWithExitCode (shell "echo 'λ'") mempty :: IO (ExitCode, Text, Text)
+    (ExitSuccess,"\955\n","")
+    > readCreateProcessWithExitCode (shell "echo 'λ'") mempty :: IO (ExitCode, String, String)
+    (ExitSuccess,"\955\n","")
+    > System.Process.Text.readCreateProcessWithExitCode (shell "yes | head -10") mempty
+    (ExitSuccess,"y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n","")
+
+Although the output *type* can be lazy, normal process functions still
+need to read until EOF on the process output before returing anything.
+If you have a process whose output never ends you can use the
+readCreateProcessLazy function to read it.  Functions like readProcess
+would block waiting for EOF on the process output:
+
+    > (Prelude.take 4 <$> readCreateProcessLazy (proc "yes" []) mempty :: IO [Chunk Text]) >>= mapM_ (putStrLn . show)
+    ProcessHandle <process>
+    Stdout "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny ..."
+    ...
+
+The output type can be any instance of ProcessOutput, instances for
+types (ExitCode, a, a), [Chunk a], and (ExitCode, [Chunk a]) are
+provided.  [Chunk a] can be converted to any other instance of
+ProcessOutput using collectOutput
+
+    > (readCreateProcess (shell "gzip -v < /proc/uptime") mempty :: IO [Chunk ByteString]) >>= mapM_ (putStrLn . show)
+    Stdout "\US\139\b\NUL\237\136\&7W\NUL\ETX345\183\&403\215\&31Q04267\177\&0\177\212\&33\225\STX\NUL_\169\142\178\ETB\NUL\NUL\NUL"
+    Stderr "gzip: stdin: file size changed while zipping\n -8.7%\n"
+    Result ExitSuccess
+    > (readCreateProcess (shell "uptime") mempty :: IO [Chunk ByteString]) >>= writeOutput
+     14:00:34 up 18 days,  7:16,  6 users,  load average: 0.04, 0.10, 0.08
+    > collectOutput <$> (readCreateProcess (shell "gzip -v < /proc/uptime") mempty :: IO [Chunk ByteString]) :: IO (ExitCode, ByteString, ByteString)
+    (ExitSuccess,"\US\139\b\NUL\185\137\&7W\NUL\ETX345\183\&427\212\&33W0426731\177\208\&35\225\STX\NUL\237\192\CAN\224\ETB\NUL\NUL\NUL","gzip: stdin: file size changed while zipping\n -8.7%\n")
+    > collectOutput <$> (readCreateProcess (shell "gzip -v < /proc/uptime") mempty :: IO [Chunk ByteString]) :: IO (ExitCode, ByteString, ByteString)
+    (ExitSuccess,"\US\139\b\NUL\185\137\&7W\NUL\ETX345\183\&427\212\&33W0426731\177\208\&35\225\STX\NUL\237\192\CAN\224\ETB\NUL\NUL\NUL","gzip: stdin: file size changed while zipping\n -8.7%\n")
+    > (collectOutput . filter (\x -> case x of Stderr _ -> False; _ -> True)) <$> (readCreateProcess (shell "gzip -v < /proc/uptime") mempty :: IO [Chunk ByteString]) :: IO (ExitCode, ByteString, ByteString)
+    (ExitSuccess,"\US\139\b\NUL<\138\&7W\NUL\ETX345\183\&410\210\&3\176P04267713\213\&37\224\STX\NULT\142\EOT\165\ETB\NUL\NUL\NUL","")
+
+Some cases that need investigation:
+
+    > (readCreateProcess (shell "gzip -v < /proc/uptime") mempty :: IO [Chunk String]) >>= mapM_ (putStrLn . show)
+    *** Exception: fd:13: hGetContents: invalid argument (invalid byte sequence)
+    > (readCreateProcess (shell "gzip -v < /proc/uptime") mempty :: IO [Chunk Text]) >>= mapM_ (putStrLn . show)
+    *** Exception: fd:13: hClose: invalid argument (Bad file descriptor)
diff --git a/process-extras.cabal b/process-extras.cabal
--- a/process-extras.cabal
+++ b/process-extras.cabal
@@ -1,5 +1,5 @@
 Name:               process-extras
-Version:            0.3.4
+Version:            0.4
 Synopsis:           Process extras
 Description:        Extends <http://hackage.haskell.org/package/process>.
                     Read process input and output as ByteStrings or
diff --git a/src/System/Process/Common.hs b/src/System/Process/Common.hs
--- a/src/System/Process/Common.hs
+++ b/src/System/Process/Common.hs
@@ -1,17 +1,15 @@
 {-# LANGUAGE CPP #-}
-{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module System.Process.Common
     ( ProcessMaker(process)
     , ListLikeProcessIO(forceOutput, readChunks)
-    , ProcessOutput(ProcessInput, pidf, outf, errf, intf, codef)
+    , ProcessOutput(pidf, outf, errf, intf, codef)
     , readProcessWithExitCode
     , readCreateProcessWithExitCode
     , readCreateProcess
@@ -65,16 +63,14 @@
       hSetBuffering errh errmode
       return (inh, outh, errh, pid)
 
-class Monoid (ProcessInput a) => ProcessOutput a where
-    type ProcessInput a
-    pidf :: ProcessHandle -> a
-    outf :: ProcessInput a -> a
-    errf :: ProcessInput a -> a
-    intf :: SomeException -> a
-    codef :: ExitCode -> a
+class Monoid b => ProcessOutput a b | b -> a where
+    pidf :: ProcessHandle -> b
+    outf :: a -> b
+    errf :: a -> b
+    intf :: SomeException -> b
+    codef :: ExitCode -> b
 
-instance ListLikeProcessIO a c => ProcessOutput (ExitCode, a, a) where
-    type ProcessInput (ExitCode, a, a) = a
+instance ListLikeProcessIO a c => ProcessOutput a (ExitCode, a, a) where
     pidf _ = mempty
     codef c = (c, mempty, mempty)
     outf x = (mempty, x, mempty)
@@ -109,7 +105,7 @@
     -> IO (ExitCode, a, a) -- ^ exitcode, stdout, stderr
 readCreateProcessWithExitCode = readCreateProcess
 
-readCreateProcess :: (ProcessMaker maker, ProcessOutput b, ListLikeProcessIO (ProcessInput b) c) => maker -> ProcessInput b -> IO b
+readCreateProcess :: (ProcessMaker maker, ProcessOutput a b, ListLikeProcessIO a c) => maker -> a -> IO b
 readCreateProcess maker input = mask $ \restore -> do
     (inh, outh, errh, pid) <- process maker
     flip onException
@@ -122,14 +118,8 @@
       -- fork off a thread to start consuming stderr
       waitErr <- forkWait $ errf <$> (hGetContents errh >>= forceOutput)
 
-      -- now write and flush any input.  Catch and ignore
-      -- ResourceVanished to avoid crashes when the process we are
-      -- writing to exits prematurely.
-      ignoreResourceVanished $ do
-        unless (ListLike.null input) $ do
-          hPutStr inh input
-          hFlush inh
-        hClose inh -- stdin has been fully written
+      -- now write and flush any input.
+      writeInput inh input
 
       -- wait on the output
       out <- waitOut
@@ -143,15 +133,8 @@
 
       return $ out <> err <> ex
 
-ignoreResourceVanished :: IO () -> IO ()
-ignoreResourceVanished action =
-    try action >>= either ignoreResourceVanished' return
-    where
-      ignoreResourceVanished' e | ioe_type e == ResourceVanished = return ()
-      ignoreResourceVanished' e = throw e
-
 -- | Like readCreateProcess, but the output is read lazily.
-readCreateProcessLazy :: (ProcessMaker maker, ProcessOutput b, ListLikeProcessIO (ProcessInput b) c) => maker -> ProcessInput b -> IO b
+readCreateProcessLazy :: (ProcessMaker maker, ProcessOutput a b, ListLikeProcessIO a c) => maker -> a -> IO b
 readCreateProcessLazy maker input = mask $ \restore -> do
     (inh, outh, errh, pid) <- process maker
     onException
@@ -167,19 +150,19 @@
           waitForProcess pid)
 
 -- | Helper function for readCreateProcessLazy.
-readInterleaved :: (ListLikeProcessIO (ProcessInput b) c, ProcessOutput b) =>
-                   [(ProcessInput b -> b, Handle)] -> IO b -> IO b
+readInterleaved :: (ListLikeProcessIO a c, ProcessOutput a b) =>
+                   [(a -> b, Handle)] -> IO b -> IO b
 readInterleaved pairs finish = newEmptyMVar >>= readInterleaved' pairs finish
 
-readInterleaved' :: forall b c. (ListLikeProcessIO (ProcessInput b) c, ProcessOutput b) =>
-                    [(ProcessInput b -> b, Handle)] -> IO b -> MVar (Either Handle b) -> IO b
+readInterleaved' :: forall a b c. (ListLikeProcessIO a c, ProcessOutput a b) =>
+                    [(a -> b, Handle)] -> IO b -> MVar (Either Handle b) -> IO b
 readInterleaved' pairs finish res = do
   mapM_ (forkIO . uncurry readHandle) pairs
   takeChunks (length pairs)
     where
       -- Forked thread to read the input and send it to takeChunks via
       -- the MVar.
-      readHandle :: (ProcessInput b -> b) -> Handle -> IO ()
+      readHandle :: (a -> b) -> Handle -> IO ()
       readHandle f h = do
         cs <- readChunks h
         -- If the type returned as stdout and stderr is lazy we need
@@ -203,13 +186,17 @@
 -- Catch and ignore Resource Vanished exceptions, they just mean the
 -- process exited before all of its output was read.
 writeInput :: ListLikeProcessIO a c => Handle -> a -> IO ()
-writeInput inh input = do
-  (do unless (null input) (hPutStr inh input >> hFlush inh)
-      hClose inh) `E.catch` resourceVanished (\ _ -> return ())
+writeInput inh input =
+    ignoreResourceVanished $ do
+      unless (ListLike.null input) $ do
+        hPutStr inh input
+        hFlush inh
+      hClose inh -- stdin has been fully written
 
 -- | Wrapper for a process that provides a handler for the
 -- ResourceVanished exception.  This is frequently an exception we
 -- wish to ignore, because many processes will deliberately exit
 -- before they have read all of their input.
-resourceVanished :: (IOError -> IO a) -> IOError -> IO a
-resourceVanished epipe e = if ioe_type e == ResourceVanished then epipe e else ioError e
+ignoreResourceVanished :: IO () -> IO ()
+ignoreResourceVanished action =
+    action `catch` (\e -> if ioe_type e == ResourceVanished then pure () else ioError e)
diff --git a/src/System/Process/ListLike.hs b/src/System/Process/ListLike.hs
--- a/src/System/Process/ListLike.hs
+++ b/src/System/Process/ListLike.hs
@@ -17,20 +17,25 @@
     , readProcessWithExitCode
     , Chunk(..)
     , collectOutput
+    , foldOutput
+    , writeOutput
     , showCreateProcessForUser
     , showCmdSpecForUser
+    , proc
+    , shell
     ) where
 
 import Control.DeepSeq (force)
 import Control.Exception as C (evaluate, SomeException, throw)
-import Data.ListLike.IO (hGetContents)
+import Data.ListLike.IO (hGetContents, hPutStr, ListLikeIO)
 #if __GLASGOW_HASKELL__ <= 709
 import Data.Monoid (mempty, mconcat)
 #endif
 import Data.Text (unpack)
 import Data.Text.Lazy (Text, toChunks)
 import System.Exit (ExitCode)
-import System.Process (CmdSpec(..), CreateProcess(..), ProcessHandle, showCommandForUser)
+import System.IO (stdout, stderr)
+import System.Process (CmdSpec(..), CreateProcess(..), proc, ProcessHandle, shell, showCommandForUser)
 import System.Process.ByteString ()
 import System.Process.ByteString.Lazy ()
 import System.Process.Common (ProcessMaker(process), ListLikeProcessIO(forceOutput, readChunks), ProcessOutput(pidf, outf, errf, codef, intf),
@@ -70,7 +75,11 @@
     | Result ExitCode
     | Exception SomeException
       -- ^ Note that the instances below do not use this constructor.
+    deriving Show
 
+instance Show ProcessHandle where
+    show _ = "<process>"
+
 instance ListLikeProcessIO a c => ProcessOutput a [Chunk a] where
     pidf p = [ProcessHandle p]
     outf x = [Stdout x]
@@ -85,11 +94,29 @@
     errf x = (mempty, [Stderr x])
     intf e = throw e
 
+foldOutput :: (ProcessHandle -> r)
+           -> (a -> r)
+           -> (a -> r)
+           -> (SomeException -> r)
+           -> (ExitCode -> r)
+           -> Chunk a
+           -> r
+foldOutput p _ _ _ _ (ProcessHandle x) = p x
+foldOutput _ o _ _ _ (Stdout x) = o x
+foldOutput _ _ e _ _ (Stderr x) = e x
+foldOutput _ _ _ i _ (Exception x) = i x
+foldOutput _ _ _ _ r (Result x) = r x
+
 -- | Turn a @[Chunk a]@ into any other instance of 'ProcessOutput'.
 collectOutput :: ProcessOutput a b => [Chunk a] -> b
-collectOutput xs = mconcat $ map (\ chunk -> case chunk of
-                                               ProcessHandle x -> pidf x
-                                               Stdout x -> outf x
-                                               Stderr x -> errf x
-                                               Result x -> codef x
-                                               Exception x -> intf x) xs
+collectOutput xs = mconcat $ map (foldOutput pidf outf errf intf codef) xs
+
+-- | Send Stdout chunks to stdout and Stderr chunks to stderr.
+writeOutput :: ListLikeIO a c => [Chunk a] -> IO ()
+writeOutput [] = pure ()
+writeOutput (x : xs) =
+    foldOutput (\_ -> pure ())
+               (hPutStr stdout)
+               (hPutStr stderr)
+               (\_ -> pure ())
+               (\_ -> pure ()) x >> writeOutput xs
