diff --git a/Data/Text.hs b/Data/Text.hs
--- a/Data/Text.hs
+++ b/Data/Text.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE BangPatterns, Rank2Types #-}
+{-# LANGUAGE BangPatterns, CPP, Rank2Types #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 -- |
@@ -167,7 +167,9 @@
                 Read(..), Show(..),
                 (&&), (||), (+), (-), (.), ($), (>>), (*),
                 div, not, return, otherwise)
+#if defined(HAVE_DEEPSEQ)
 import Control.DeepSeq (NFData)
+#endif
 import Control.Exception (assert)
 import Data.Char (isSpace)
 import Control.Monad (foldM)
@@ -215,7 +217,9 @@
 instance IsString Text where
     fromString = pack
 
+#if defined(HAVE_DEEPSEQ)
 instance NFData Text
+#endif
 
 -- -----------------------------------------------------------------------------
 -- * Conversion to/from 'Text'
diff --git a/Data/Text/Encoding/Error.hs b/Data/Text/Encoding/Error.hs
--- a/Data/Text/Encoding/Error.hs
+++ b/Data/Text/Encoding/Error.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE CPP, DeriveDataTypeable #-}
 -- |
 -- Module      : Data.Text.Encoding.Error
 -- Copyright   : (c) Bryan O'Sullivan 2009
@@ -35,7 +35,11 @@
     , replace
     ) where
 
+#if __GLASGOW_HASKELL__ >= 610
 import Control.Exception (Exception, throw)
+#else
+import Control.Exception.Extensible (Exception, throw)
+#endif
 import Data.Typeable (Typeable)
 import Data.Word (Word8)
 import Numeric (showHex)
diff --git a/Data/Text/Lazy.hs b/Data/Text/Lazy.hs
--- a/Data/Text/Lazy.hs
+++ b/Data/Text/Lazy.hs
@@ -1,5 +1,5 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BangPatterns, CPP #-}
 -- |
 -- Module      : Data.Text.Lazy
 -- Copyright   : (c) Bryan O'Sullivan 2009
@@ -169,7 +169,9 @@
                 (&&), (+), (-), (.), ($), (++),
                 div, flip, fromIntegral, not, otherwise)
 import qualified Prelude as P
+#if defined(HAVE_DEEPSEQ)
 import Control.DeepSeq (NFData(..))
+#endif
 import Data.Int (Int64)
 import qualified Data.List as L
 import Data.Char (isSpace)
@@ -208,9 +210,11 @@
 instance IsString Text where
     fromString = pack
 
+#if defined(HAVE_DEEPSEQ)
 instance NFData Text where
     rnf Empty        = ()
     rnf (Chunk _ ts) = rnf ts
+#endif
 
 -- | /O(n)/ Convert a 'String' into a 'Text'.
 --
diff --git a/Data/Text/Lazy/Encoding/Fusion.hs b/Data/Text/Lazy/Encoding/Fusion.hs
--- a/Data/Text/Lazy/Encoding/Fusion.hs
+++ b/Data/Text/Lazy/Encoding/Fusion.hs
@@ -35,7 +35,6 @@
 import Data.Text.Encoding.Fusion.Common
 import Data.Text.Encoding.Error
 import Data.Text.Fusion (Step(..), Stream(..))
-import Data.Text.Fusion.Internal (M(..), PairS(..), S(..))
 import Data.Text.Fusion.Size
 import Data.Text.UnsafeChar (unsafeChr8)
 import Data.Word (Word8)
@@ -47,51 +46,54 @@
 import Control.Exception (assert)
 import qualified Data.ByteString.Internal as B
 
+data S = S0
+       | S1 {-# UNPACK #-} !Word8
+       | S2 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
+       | S3 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
+       | S4 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
+
+data T = T {-# UNPACK #-} !ByteString {-# UNPACK #-} !S {-# UNPACK #-} !Int
+
 -- | /O(n)/ Convert a lazy 'ByteString' into a 'Stream Char', using
 -- UTF-8 encoding.
 streamUtf8 :: OnDecodeError -> ByteString -> Stream Char
-streamUtf8 onErr bs0 = Stream next (bs0 :*: empty :*: 0) unknownSize
-    where
-      empty = S N N N N
-      next (bs@(Chunk ps _) :*: S N _ _ _ :*: i)
-          | i < len && U8.validate1 a =
-              Yield (unsafeChr8 a) (bs :*: empty :*: i+1)
-          | i + 1 < len && U8.validate2 a b =
-              Yield (U8.chr2 a b) (bs :*: empty :*: i+2)
-          | i + 2 < len && U8.validate3 a b c =
-              Yield (U8.chr3 a b c) (bs :*: empty :*: i+3)
-          | i + 4 < len && U8.validate4 a b c d =
-              Yield (U8.chr4 a b c d) (bs :*: empty :*: i+4)
-          where len = B.length ps
-                a = B.unsafeIndex ps i
-                b = B.unsafeIndex ps (i+1)
-                c = B.unsafeIndex ps (i+2)
-                d = B.unsafeIndex ps (i+3)
-      next st@(bs :*: s :*: i) =
-        case s of
-          S (J a) N _ _             | U8.validate1 a ->
-            Yield (unsafeChr8 a) es
-          S (J a) (J b) N _         | U8.validate2 a b ->
-            Yield (U8.chr2 a b) es
-          S (J a) (J b) (J c) N     | U8.validate3 a b c ->
-            Yield (U8.chr3 a b c) es
-          S (J a) (J b) (J c) (J d) | U8.validate4 a b c d ->
-            Yield (U8.chr4 a b c d) es
-          _ -> consume st
-         where es = bs :*: empty :*: i
-      consume (bs@(Chunk ps rest) :*: s :*: i)
-          | i >= B.length ps = consume (rest :*: s  :*: 0)
-          | otherwise =
-        case s of
-          S N _ _ _ -> next (bs :*: S x N N N :*: i+1)
-          S a N _ _ -> next (bs :*: S a x N N :*: i+1)
-          S a b N _ -> next (bs :*: S a b x N :*: i+1)
-          S a b c N -> next (bs :*: S a b c x :*: i+1)
-          S (J a) b c d -> decodeError "streamUtf8" "UTF-8" onErr (Just a)
-                           (bs :*: S b c d N :*: i+1)
-          where x = J (B.unsafeIndex ps i)
-      consume (Empty :*: S N _ _ _ :*: _) = Done
-      consume st = decodeError "streamUtf8" "UTF-8" onErr Nothing st
+streamUtf8 onErr bs0 = Stream next (T bs0 S0 0) unknownSize
+  where
+    next (T bs@(Chunk ps _) S0 i)
+      | i < len && U8.validate1 a =
+          Yield (unsafeChr8 a)    (T bs S0 (i+1))
+      | i + 1 < len && U8.validate2 a b =
+          Yield (U8.chr2 a b)     (T bs S0 (i+2))
+      | i + 2 < len && U8.validate3 a b c =
+          Yield (U8.chr3 a b c)   (T bs S0 (i+3))
+      | i + 4 < len && U8.validate4 a b c d =
+          Yield (U8.chr4 a b c d) (T bs S0 (i+4))
+      where len = B.length ps
+            a = B.unsafeIndex ps i
+            b = B.unsafeIndex ps (i+1)
+            c = B.unsafeIndex ps (i+2)
+            d = B.unsafeIndex ps (i+3)
+    next st@(T bs s i) =
+      case s of
+        S1 a       | U8.validate1 a       -> Yield (unsafeChr8 a)    es
+        S2 a b     | U8.validate2 a b     -> Yield (U8.chr2 a b)     es
+        S3 a b c   | U8.validate3 a b c   -> Yield (U8.chr3 a b c)   es
+        S4 a b c d | U8.validate4 a b c d -> Yield (U8.chr4 a b c d) es
+        _ -> consume st
+       where es = T bs S0 i
+    consume (T bs@(Chunk ps rest) s i)
+        | i >= B.length ps = consume (T rest s 0)
+        | otherwise =
+      case s of
+        S0         -> next (T bs (S1 x)       (i+1))
+        S1 a       -> next (T bs (S2 a x)     (i+1))
+        S2 a b     -> next (T bs (S3 a b x)   (i+1))
+        S3 a b c   -> next (T bs (S4 a b c x) (i+1))
+        S4 a b c d -> decodeError "streamUtf8" "UTF-8" onErr (Just a)
+                           (T bs (S3 b c d)   (i+1))
+        where x = B.unsafeIndex ps i
+    consume (T Empty S0 _) = Done
+    consume st             = decodeError "streamUtf8" "UTF-8" onErr Nothing st
 {-# INLINE [0] streamUtf8 #-}
 
 -- | /O(n)/ Convert a 'Stream' 'Word8' to a lazy 'ByteString'.
diff --git a/Data/Text/Lazy/IO.hs b/Data/Text/Lazy/IO.hs
--- a/Data/Text/Lazy/IO.hs
+++ b/Data/Text/Lazy/IO.hs
@@ -8,7 +8,7 @@
 -- Stability   : experimental
 -- Portability : GHC
 --
--- Efficient locale-sensitive support for text I\/O.
+-- Efficient locale-sensitive support for lazy text I\/O.
 
 module Data.Text.Lazy.IO
     (
@@ -31,7 +31,7 @@
     , putStrLn
     ) where
 
-import Data.Text.Lazy.Internal (Text(..))
+import Data.Text.Lazy (Text)
 import Prelude hiding (appendFile, getContents, getLine, interact, putStr,
                        putStrLn, readFile, writeFile)
 import System.IO (Handle, IOMode(..), hPutChar, openFile, stdin, stdout,
@@ -39,13 +39,14 @@
 import qualified Data.Text.IO as T
 import qualified Data.Text.Lazy as L
 #if __GLASGOW_HASKELL__ <= 610
-import Data.Text.Lazy.Encoding (decodeUtf8, encodeUtf8)
+import Data.Text.Lazy.Encoding (decodeUtf8)
 import qualified Data.ByteString.Char8 as S8
 import qualified Data.ByteString.Lazy.Char8 as L8
 #else
 import Control.Exception (throw)
 import Data.IORef (readIORef)
 import Data.Text.IO.Internal (hGetLineWith, readChunk)
+import Data.Text.Lazy.Internal (chunk, empty)
 import GHC.IO.Buffer (isEmptyBuffer)
 import GHC.IO.Exception (IOException(..), IOErrorType(..), ioException)
 import GHC.IO.Handle.Internals (augmentIOError, hClose_help,
@@ -55,9 +56,8 @@
 import System.IO.Unsafe (unsafeInterleaveIO)
 #endif
 
--- | The 'readFile' function reads a file and returns the contents of
--- the file as a string.  The entire file is read strictly, as with
--- 'getContents'.
+-- | Read a file and return its contents as a string.  The file is
+-- read lazily, as with 'getContents'.
 readFile :: FilePath -> IO Text
 readFile name = openFile name ReadMode >>= hGetContents
 
@@ -70,6 +70,8 @@
 appendFile :: FilePath -> Text -> IO ()
 appendFile p = withFile p AppendMode . flip hPutStr
 
+-- | Lazily read the remaining contents of a 'Handle'.  The 'Handle'
+-- will be closed after the read completes, or on error.
 hGetContents :: Handle -> IO Text
 #if __GLASGOW_HASKELL__ <= 610
 hGetContents = fmap decodeUtf8 . L8.hGetContents
@@ -94,11 +96,11 @@
    buf <- readIORef haCharBuffer
    (do t <- readChunk hh buf
        ts <- lazyRead h
-       return (hh, Chunk t ts)) `catch` \e -> do
+       return (hh, chunk t ts)) `catch` \e -> do
          (hh', _) <- hClose_help hh
          if isEOFError e
            then return $ if isEmptyBuffer buf
-                         then (hh', Empty)
+                         then (hh', empty)
                          else (hh', L.singleton '\r')
            else throw (augmentIOError e "hGetContents" h)
 #endif
@@ -121,12 +123,12 @@
 
 -- | The 'interact' function takes a function of type @Text -> Text@
 -- as its argument. The entire input from the standard input device is
--- passed to this function as its argument, and the resulting string
--- is output on the standard output device.
+-- passed (lazily) to this function as its argument, and the resulting
+-- string is output on the standard output device.
 interact :: (Text -> Text) -> IO ()
 interact f = putStr . f =<< getContents
 
--- | Read all user input on 'stdin' as a single string.
+-- | Lazily read all user input on 'stdin' as a single string.
 getContents :: IO Text
 getContents = hGetContents stdin
 
diff --git a/tests/Benchmarks.hs b/tests/Benchmarks.hs
--- a/tests/Benchmarks.hs
+++ b/tests/Benchmarks.hs
@@ -9,11 +9,14 @@
 import Data.Char
 import qualified Codec.Binary.UTF8.Generic as UTF8
 import qualified Data.Text as TS
+import qualified Data.Text.IO as TS
 import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.IO as TL
 import qualified Data.List as L
 import qualified Data.Text.Encoding as TS
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Criterion.MultiMap as M
+import Control.DeepSeq
 import Criterion.Config
 import GHC.Base
 
@@ -38,7 +41,8 @@
     rnf (B b) = rnf b
 
 main = do
-  bsa <- BS.readFile "text/test/russian.txt"
+  let dataFile = "text/test/russian.txt"
+  bsa <- BS.readFile dataFile
   let tsa     = TS.decodeUtf8 bsa
       tsb     = TS.toUpper tsa
       tla     = TL.fromChunks (TS.chunksOf 16376 tsa)
@@ -191,6 +195,13 @@
       , bench "bs" $ nf (BS.map f . BS.map f) bsa
       , bench "bl" $ nf (BL.map f . BL.map f) bla
       , bench "l" $ nf (L.map f . L.map f) la
+      ],
+      bgroup "readFile" [
+        bench "ts" $ TS.readFile dataFile
+      , bench "tl" $ nfIO (TL.readFile dataFile)
+      , bench "bs" $ BS.readFile dataFile
+      , bench "bl" $ nfIO (BL.length `fmap` BL.readFile dataFile)
+      , bench "l" $ nfIO (length `fmap` readFile dataFile)
       ],
       bgroup "replicate char" [
         bench "ts" $ nf (TS.replicate bsa_len) (TS.singleton c)
diff --git a/text.cabal b/text.cabal
--- a/text.cabal
+++ b/text.cabal
@@ -1,5 +1,5 @@
 name:           text
-version:        0.7
+version:        0.7.0.1
 synopsis:       An efficient packed Unicode text type
 description:    An efficient packed Unicode text type.
 license:        BSD3
@@ -11,7 +11,7 @@
 copyright:      2008-2009 Tom Harper, 2009 Bryan O'Sullivan
 category:       Data, Text
 build-type:     Simple
-cabal-version:  >= 1.2
+cabal-version:  >= 1.2.3
 extra-source-files:
     README
     TODO
@@ -67,11 +67,14 @@
 
   build-depends:
     base       < 5,
-    bytestring >= 0.9 && < 1.0,
-    deepseq    >= 1.1.0.0
+    bytestring >= 0.9 && < 1.0
   if impl(ghc >= 6.10)
     build-depends:
-      ghc-prim, base >= 4
+      ghc-prim, base >= 4, deepseq >= 1.1.0.0
+    cpp-options: -DHAVE_DEEPSEQ
+  else
+    build-depends: extensible-exceptions
+    extensions: PatternSignatures
 
   -- gather extensive profiling data for now
   ghc-prof-options: -auto-all
