diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,12 @@
 # Revision history for execpath
 
-## 0.1.0.0  -- YYYY-mm-dd
+## 0.1.0.1 -- 2019-01-22
+
+* Force output of lazy read functions to normal form to prevent
+  people from accidentally holding onto resources.
+
+* New helper functions for reading.
+  
+## 0.1.0.0  -- 2018-11-02
 
 * First version. Released on an unsuspecting world.
diff --git a/shh.cabal b/shh.cabal
--- a/shh.cabal
+++ b/shh.cabal
@@ -1,5 +1,5 @@
 name:                shh
-version:             0.1.0.0
+version:             0.1.0.1
 synopsis:            Simple shell scripting from Haskell
 description:         Provides a shell scripting environment for Haskell. It
                      helps you all external binaries, and allows you to
@@ -21,9 +21,8 @@
 
 library
   exposed-modules:     Shh
-                     , Shh.Example
                      , Shh.Internal
-  build-depends:       base >=4.9 && <4.12
+  build-depends:       base >=4.9 && <4.13
                      , async
                      , deepseq
                      , directory
diff --git a/src/Shh.hs b/src/Shh.hs
--- a/src/Shh.hs
+++ b/src/Shh.hs
@@ -1,11 +1,3 @@
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE DefaultSignatures #-}
-{-# LANGUAGE DeriveFunctor #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE GADTs #-}
 -- | Shh provides a shell-like environment for Haskell.
 module Shh
     ( initInteractive
@@ -22,19 +14,34 @@
     , Stream(..)
     , devNull
     -- | == Reading @stdout@
-    , readProc
+    -- === Lazy/Streaming reads
+    -- These reads are lazy. The process is run long enough to produce
+    -- the amount of output that is actually used. It is therefor suitable
+    -- for use with infinite output streams. The process is terminated
+    -- as soon the function finishes. Note that the result is forced to
+    -- normal form to prevent any accidental reading after the process has
+    -- terminated.
     , withRead
-    , trim
+    , withReadSplit0
+    , withReadLines
+    , withReadWords
+    -- | === Strict reads
+    , readProc
     , readTrim
-    , split0
     , readSplit0
     , readLines
+    , readWords
     , readAuto
+    -- | === String manipulation
+    -- Utility functions for dealing with common string issues in shell
+    -- scripting.
+    , trim
+    , split0
     -- | == Writing to @stdin@
     , writeProc
     , (<<<), (>>>)
     , readWriteProc
-    , mapP
+    , apply
     -- | == Exceptions
     -- If any exception is allowed to propagate out of a pipeline, all the
     -- processes comprising the pipeline will be terminated. This is contrary
diff --git a/src/Shh/Example.hs b/src/Shh/Example.hs
deleted file mode 100644
--- a/src/Shh/Example.hs
+++ /dev/null
@@ -1,7 +0,0 @@
-{-# LANGUAGE TemplateHaskell #-}
-
-module Shh.Example where
-
-import Shh
-
-$(loadAnnotatedEnv (\s -> s ++ "_"))
diff --git a/src/Shh/Internal.hs b/src/Shh/Internal.hs
--- a/src/Shh/Internal.hs
+++ b/src/Shh/Internal.hs
@@ -12,7 +12,7 @@
 
 
 import Control.Concurrent.Async
-import Control.DeepSeq (rnf)
+import Control.DeepSeq (rnf,force,NFData)
 import Control.Exception as C
 import Control.Monad
 import Control.Monad.IO.Class
@@ -217,27 +217,34 @@
 -- This is strict, so the whole output is read into a `String`. See `withRead`
 -- for a lazy version that can be used for streaming.
 readProc :: MonadIO io => Proc a -> io String
-readProc (Proc f) = liftIO $ do
-    (r,w) <- createPipe
-    (_,o) <- concurrently
-        (f stdin w stderr (pure ()) (hClose w))
-        (do
-            output <- hGetContents r
-            C.evaluate $ rnf output
-            hClose r
-            pure output
-        )
-    pure o
+readProc p = withRead p pure
 
 -- | Run a process and capture it's output lazily. Once the continuation
 -- is completed, the handles are closed, and the process is terminated.
-withRead :: MonadIO io => Proc a -> (String -> IO b) -> io b
+withRead :: (NFData b, MonadIO io) => Proc a -> (String -> IO b) -> io b
 withRead (Proc f) k = liftIO $ 
     withPipe $ \r w -> do
         withAsync (f stdin w stderr (pure ()) (hClose w)) $ \_ ->
-            (hGetContents r >>= k) `finally` hClose r
+            (hGetContents r >>= k >>= C.evaluate . force) `finally` hClose r
 
+-- | Apply a transformation function to the string before the IO action.
+withRead' :: (NFData b, MonadIO io) => (String -> a) -> Proc x -> (a -> IO b) -> io b
+withRead' f p io = withRead p (io . f)
 
+-- | Like @'withRead'@ except it splits the string with @'split0'@ first.
+withReadSplit0 :: (NFData b, MonadIO io) => Proc a -> ([String] -> IO b) -> io b
+withReadSplit0 = withRead' split0
+
+-- | Like @'withRead'@ except it splits the string with @'lines'@ first.
+--
+-- NB: Please consider using @'withReadSplit0'@ where you can.
+withReadLines :: (NFData b, MonadIO io) => Proc a -> ([String] -> IO b) -> io b
+withReadLines = withRead' lines
+
+-- | Like @'withRead'@ except it splits the string with @'words'@ first.
+withReadWords :: (NFData b, MonadIO io) => Proc a -> ([String] -> IO b) -> io b
+withReadWords = withRead' words
+
 -- | Read and write to a `Proc`...
 readWriteProc :: MonadIO io => Proc a -> String -> io String
 readWriteProc (Proc f) input = liftIO $ do
@@ -255,12 +262,12 @@
         )
     pure o
 
--- | Some as `readWriteProc`. Map a `Proc` over a `String`.
+-- | Some as `readWriteProc`. Apply a `Proc` to a `String`.
 --
--- >>> mapP shasum "Hello"
+-- >>> apply shasum "Hello"
 -- "f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0  -\n"
-mapP :: MonadIO io => Proc a -> String -> io String
-mapP = readWriteProc
+apply :: MonadIO io => Proc a -> String -> io String
+apply = readWriteProc
 
 -- | Provide the stdin of a `Proc` from a `String`
 writeProc :: MonadIO io => Proc a -> String -> io a
@@ -328,7 +335,6 @@
 readTrim :: MonadIO io => Proc a -> io String
 readTrim = fmap trim . readProc
 
-
 -- | A class for things that can be converted to arguments on the command
 -- line. The default implementation is to use `show`.
 class ExecArg a where
@@ -440,7 +446,8 @@
             | validIdentifier (f s) = Just (f s, s)
             | otherwise             = Nothing
 
--- | Function that splits '\0' seperated list of strings.
+-- | Function that splits '\0' seperated list of strings. Useful in conjuction
+-- with @find . "-print0"@.
 split0 :: String -> [String]
 split0 = endBy "\0"
 
@@ -449,11 +456,17 @@
 --
 -- > readSplit0 $ find "-print0"
 readSplit0 :: Proc () -> IO [String]
-readSplit0 p = split0 <$> readProc p
+readSplit0 p = withReadSplit0 p pure
 
 -- | A convinience function for reading the output lines of a `Proc`.
+--
+-- Note: Please consider using @'readSplit0'@ instead if you can.
 readLines :: Proc () -> IO [String]
-readLines p = lines <$> readProc p
+readLines p = withReadLines p pure
+
+-- | Read output into a list of words
+readWords :: Proc () -> IO [String]
+readWords p = withReadWords p pure
 
 -- | Like `readProc`, but attempts to `Prelude.read` the result.
 readAuto :: Read a => Proc () -> IO a
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -70,8 +70,8 @@
         writeProc (cat &> Truncate t) "Goodbye"
         r <- readProc (cat t)
         r @?= "Goodbye"
-    , testCase "mapP" $ do
-        r <- mapP shasum "test"
+    , testCase "apply" $ do
+        r <- apply shasum "test"
         r @?= "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3  -\n"
     , testCase "ignoreFailure" $ do
         r <- readProc $ ignoreFailure false |> echo "Hello"
