diff --git a/Data/Text.hs b/Data/Text.hs
--- a/Data/Text.hs
+++ b/Data/Text.hs
@@ -1,10 +1,18 @@
-{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples #-}
+{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples, TypeFamilies #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 #if __GLASGOW_HASKELL__ >= 702
 {-# LANGUAGE Trustworthy #-}
 #endif
-#if __GLASGOW_HASKELL__ >= 708
-{-# LANGUAGE TypeFamilies #-}
+-- Using TemplateHaskell in text unconditionally is unacceptable, as
+-- it's a GHC boot library. TemplateHaskellQuotes was added in 8.0, so
+-- this would seem to be a problem. However, GHC's policy of only
+-- needing to be able to compile itself from the last few releases
+-- allows us to use full-fat TH on older versions, while using THQ for
+-- GHC versions that may be used for bootstrapping.
+#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TemplateHaskellQuotes #-}
+#else
+{-# LANGUAGE TemplateHaskell #-}
 #endif
 
 -- |
@@ -235,15 +243,18 @@
 import qualified Data.Text.Internal.Functions as F
 import qualified Data.Text.Internal.Encoding.Utf16 as U16
 import Data.Text.Internal.Search (indices)
+import Data.Text.Internal.Unsafe.Shift (UnsafeShift(..))
 #if defined(__HADDOCK__)
 import Data.ByteString (ByteString)
 import qualified Data.Text.Lazy as L
 import Data.Int (Int64)
 #endif
 import GHC.Base (eqInt, neInt, gtInt, geInt, ltInt, leInt)
-#if __GLASGOW_HASKELL__ >= 708
+#if MIN_VERSION_base(4,7,0)
 import qualified GHC.Exts as Exts
 #endif
+import qualified Language.Haskell.TH.Lib as TH
+import Language.Haskell.TH.Syntax (Lift, lift)
 #if MIN_VERSION_base(4,7,0)
 import Text.Printf (PrintfArg, formatArg, formatString)
 #endif
@@ -371,7 +382,7 @@
 instance IsString Text where
     fromString = pack
 
-#if __GLASGOW_HASKELL__ >= 708
+#if MIN_VERSION_base(4,7,0)
 -- | @since 1.2.0.0
 instance Exts.IsList Text where
     type Item Text = Char
@@ -413,6 +424,13 @@
     _ -> P.error "gunfold"
   dataTypeOf _ = textDataType
 
+-- | This instance has similar considerations to the 'Data' instance:
+-- it preserves abstraction at the cost of inefficiency.
+--
+-- @since 1.2.4.0
+instance Lift Text where
+  lift = TH.appE (TH.varE 'pack) . TH.stringE . unpack
+
 #if MIN_VERSION_base(4,7,0)
 -- | Only defined for @base-4.7.0.0@ and later
 --
@@ -605,7 +623,7 @@
 -- Subject to fusion.
 length :: Text -> Int
 length t = S.length (stream t)
-{-# INLINE [0] length #-}
+{-# INLINE [1] length #-}
 -- length needs to be phased after the compareN/length rules otherwise
 -- it may inline before the rules have an opportunity to fire.
 
@@ -702,7 +720,7 @@
 -- >>> T.reverse "desrever"
 -- "reversed"
 --
--- Subject to fusion.
+-- Subject to fusion (fuses with its argument).
 reverse :: Text -> Text
 reverse t = S.reverse (stream t)
 {-# INLINE reverse #-}
@@ -1033,8 +1051,7 @@
 {-# INLINE scanl #-}
 
 -- | /O(n)/ 'scanl1' is a variant of 'scanl' that has no starting
--- value argument.  Subject to fusion.  Performs replacement on
--- invalid scalar values.
+-- value argument. Performs replacement on invalid scalar values.
 --
 -- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]
 scanl1 :: (Char -> Char -> Char) -> Text -> Text
@@ -1052,8 +1069,7 @@
 {-# INLINE scanr #-}
 
 -- | /O(n)/ 'scanr1' is a variant of 'scanr' that has no starting
--- value argument.  Subject to fusion.  Performs replacement on
--- invalid scalar values.
+-- value argument. Performs replacement on invalid scalar values.
 scanr1 :: (Char -> Char -> Char) -> Text -> Text
 scanr1 f t | null t    = empty
            | otherwise = scanr f (last t) (init t)
@@ -1091,16 +1107,19 @@
     | isSingleton t          = replicateChar n (unsafeHead t)
     | otherwise              = Text (A.run x) 0 len
   where
-    len = l `mul` n
+    len = l `mul` n -- TODO: detect overflows
     x :: ST s (A.MArray s)
     x = do
       arr <- A.new len
-      let loop !d !i | i >= n    = return arr
-                     | otherwise = let m = d + l
-                                   in A.copyI arr d a o m >> loop m (i+1)
-      loop 0 0
+      A.copyI arr 0 a o l
+      let loop !l1 =
+            let rest = len - l1 in
+            if rest <= l1 then A.copyM arr l1 arr 0 rest >> return arr
+            else A.copyM arr l1 arr 0 l1 >> loop (l1 `shiftL` 1)
+      loop l
 {-# INLINE [1] replicate #-}
 
+
 {-# RULES
 "TEXT replicate/singleton -> replicateChar" [~1] forall n c.
     replicate n (singleton c) = replicateChar n c
@@ -1237,7 +1256,7 @@
 
 -- | /O(n)/ 'takeWhileEnd', applied to a predicate @p@ and a 'Text',
 -- returns the longest suffix (possibly empty) of elements that
--- satisfy @p@.  Subject to fusion.
+-- satisfy @p@.
 -- Examples:
 --
 -- >>> takeWhileEnd (=='o') "foo"
@@ -1252,13 +1271,6 @@
             where (c,d)        = reverseIter t i
 {-# INLINE [1] takeWhileEnd #-}
 
-{-# RULES
-"TEXT takeWhileEnd -> fused" [~1] forall p t.
-    takeWhileEnd p t = S.reverse (S.takeWhile p (S.reverseStream t))
-"TEXT takeWhileEnd -> unfused" [1] forall p t.
-    S.reverse (S.takeWhile p (S.reverseStream t)) = takeWhileEnd p t
-  #-}
-
 -- | /O(n)/ 'dropWhile' @p@ @t@ returns the suffix remaining after
 -- 'takeWhile' @p@ @t@. Subject to fusion.
 dropWhile :: (Char -> Bool) -> Text -> Text
@@ -1278,7 +1290,7 @@
 
 -- | /O(n)/ 'dropWhileEnd' @p@ @t@ returns the prefix remaining after
 -- dropping characters that satisfy the predicate @p@ from the end of
--- @t@.  Subject to fusion.
+-- @t@.
 --
 -- Examples:
 --
@@ -1292,13 +1304,6 @@
             where (c,d)        = reverseIter t i
 {-# INLINE [1] dropWhileEnd #-}
 
-{-# RULES
-"TEXT dropWhileEnd -> fused" [~1] forall p t.
-    dropWhileEnd p t = S.reverse (S.dropWhile p (S.reverseStream t))
-"TEXT dropWhileEnd -> unfused" [1] forall p t.
-    S.reverse (S.dropWhile p (S.reverseStream t)) = dropWhileEnd p t
-  #-}
-
 -- | /O(n)/ 'dropAround' @p@ @t@ returns the substring remaining after
 -- dropping characters that satisfy the predicate @p@ from both the
 -- beginning and end of @t@.  Subject to fusion.
@@ -1311,7 +1316,7 @@
 -- > dropWhile isSpace
 stripStart :: Text -> Text
 stripStart = dropWhile isSpace
-{-# INLINE [1] stripStart #-}
+{-# INLINE stripStart #-}
 
 -- | /O(n)/ Remove trailing white space from a string.  Equivalent to:
 --
@@ -1482,7 +1487,7 @@
 
 -- | /O(n)/ The 'find' function takes a predicate and a 'Text', and
 -- returns the first element matching the predicate, or 'Nothing' if
--- there is no such element.
+-- there is no such element. Subject to fusion.
 find :: (Char -> Bool) -> Text -> Maybe Char
 find p t = S.findBy p (stream t)
 {-# INLINE find #-}
@@ -1598,7 +1603,7 @@
 -- searching for the index of @\"::\"@ and taking the substrings
 -- before and after that index, you would instead use @breakOnAll \"::\"@.
 
--- | /O(n)/ 'Text' index (subscript) operator, starting from 0.
+-- | /O(n)/ 'Text' index (subscript) operator, starting from 0. Subject to fusion.
 index :: Text -> Int -> Char
 index t n = S.index (stream t) n
 {-# INLINE index #-}
diff --git a/Data/Text/Array.hs b/Data/Text/Array.hs
--- a/Data/Text/Array.hs
+++ b/Data/Text/Array.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, Rank2Types,
+{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types,
     RecordWildCards, UnboxedTuples, UnliftedFFITypes #-}
 {-# OPTIONS_GHC -fno-warn-unused-matches #-}
 -- |
@@ -58,7 +58,7 @@
 #if defined(ASSERTS)
 import Control.Exception (assert)
 #endif
-#if __GLASGOW_HASKELL__ >= 702
+#if MIN_VERSION_base(4,4,0)
 import Control.Monad.ST.Unsafe (unsafeIOToST)
 #else
 import Control.Monad.ST (unsafeIOToST)
@@ -66,7 +66,7 @@
 import Data.Bits ((.&.), xor)
 import Data.Text.Internal.Unsafe (inlinePerformIO)
 import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
-#if __GLASGOW_HASKELL__ >= 703
+#if MIN_VERSION_base(4,5,0)
 import Foreign.C.Types (CInt(CInt), CSize(CSize))
 #else
 import Foreign.C.Types (CInt, CSize)
diff --git a/Data/Text/Encoding.hs b/Data/Text/Encoding.hs
--- a/Data/Text/Encoding.hs
+++ b/Data/Text/Encoding.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving, MagicHash,
+{-# LANGUAGE BangPatterns, CPP, GeneralizedNewtypeDeriving, MagicHash,
     UnliftedFFITypes #-}
 #if __GLASGOW_HASKELL__ >= 702
 {-# LANGUAGE Trustworthy #-}
@@ -59,7 +59,7 @@
     , encodeUtf8BuilderEscaped
     ) where
 
-#if __GLASGOW_HASKELL__ >= 702
+#if MIN_VERSION_base(4,4,0)
 import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
 #else
 import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
@@ -78,7 +78,7 @@
 import Data.Text.Show ()
 import Data.Text.Unsafe (unsafeDupablePerformIO)
 import Data.Word (Word8, Word32)
-#if __GLASGOW_HASKELL__ >= 703
+#if MIN_VERSION_base(4,5,0)
 import Foreign.C.Types (CSize(CSize))
 #else
 import Foreign.C.Types (CSize)
diff --git a/Data/Text/Foreign.hs b/Data/Text/Foreign.hs
--- a/Data/Text/Foreign.hs
+++ b/Data/Text/Foreign.hs
@@ -34,7 +34,7 @@
 #if defined(ASSERTS)
 import Control.Exception (assert)
 #endif
-#if __GLASGOW_HASKELL__ >= 702
+#if MIN_VERSION_base(4,4,0)
 import Control.Monad.ST.Unsafe (unsafeIOToST)
 #else
 import Control.Monad.ST (unsafeIOToST)
diff --git a/Data/Text/Internal/Functions.hs b/Data/Text/Internal/Functions.hs
--- a/Data/Text/Internal/Functions.hs
+++ b/Data/Text/Internal/Functions.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE CPP, DeriveDataTypeable #-}
-
 -- |
 -- Module      : Data.Text.Internal.Functions
 -- Copyright   : 2010 Bryan O'Sullivan
diff --git a/Data/Text/Internal/Fusion/Common.hs b/Data/Text/Internal/Fusion/Common.hs
--- a/Data/Text/Internal/Fusion/Common.hs
+++ b/Data/Text/Internal/Fusion/Common.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE PatternGuards, BangPatterns, MagicHash, Rank2Types #-}
+{-# LANGUAGE BangPatterns, MagicHash, Rank2Types #-}
 -- |
 -- Module      : Data.Text.Internal.Fusion.Common
 -- Copyright   : (c) Bryan O'Sullivan 2009, 2012
diff --git a/Data/Text/Internal/Fusion/Size.hs b/Data/Text/Internal/Fusion/Size.hs
--- a/Data/Text/Internal/Fusion/Size.hs
+++ b/Data/Text/Internal/Fusion/Size.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, PatternGuards #-}
+{-# LANGUAGE CPP #-}
 {-# OPTIONS_GHC -fno-warn-missing-methods #-}
 -- |
 -- Module      : Data.Text.Internal.Fusion.Internal
diff --git a/Data/Text/Internal/IO.hs b/Data/Text/Internal/IO.hs
--- a/Data/Text/Internal/IO.hs
+++ b/Data/Text/Internal/IO.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE BangPatterns, CPP, RecordWildCards #-}
+{-# LANGUAGE BangPatterns, RecordWildCards #-}
 -- |
 -- Module      : Data.Text.Internal.IO
 -- Copyright   : (c) 2009, 2010 Bryan O'Sullivan,
diff --git a/Data/Text/Lazy.hs b/Data/Text/Lazy.hs
--- a/Data/Text/Lazy.hs
+++ b/Data/Text/Lazy.hs
@@ -1,10 +1,18 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# LANGUAGE BangPatterns, MagicHash, CPP #-}
+{-# LANGUAGE BangPatterns, MagicHash, CPP, TypeFamilies #-}
 #if __GLASGOW_HASKELL__ >= 702
 {-# LANGUAGE Trustworthy #-}
 #endif
-#if __GLASGOW_HASKELL__ >= 708
-{-# LANGUAGE TypeFamilies #-}
+-- Using TemplateHaskell in text unconditionally is unacceptable, as
+-- it's a GHC boot library. TemplateHaskellQuotes was added in 8.0, so
+-- this would seem to be a problem. However, GHC's policy of only
+-- needing to be able to compile itself from the last few releases
+-- allows us to use full-fat TH on older versions, while using THQ for
+-- GHC versions that may be used for bootstrapping.
+#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TemplateHaskellQuotes #-}
+#else
+{-# LANGUAGE TemplateHaskell #-}
 #endif
 
 -- |
@@ -235,10 +243,12 @@
 #else
 import qualified GHC.Base as GHC
 #endif
-#if __GLASGOW_HASKELL__ >= 708
+#if MIN_VERSION_base(4,7,0)
 import qualified GHC.Exts as Exts
 #endif
 import GHC.Prim (Addr#)
+import qualified Language.Haskell.TH.Lib as TH
+import Language.Haskell.TH.Syntax (Lift, lift)
 #if MIN_VERSION_base(4,7,0)
 import Text.Printf (PrintfArg, formatArg, formatString)
 #endif
@@ -365,7 +375,7 @@
 instance IsString Text where
     fromString = pack
 
-#if __GLASGOW_HASKELL__ >= 708
+#if MIN_VERSION_base(4,7,0)
 -- | @since 1.2.0.0
 instance Exts.IsList Text where
     type Item Text = Char
@@ -399,6 +409,13 @@
     _ -> error "Data.Text.Lazy.Text.gunfold"
   dataTypeOf _   = textDataType
 
+-- | This instance has similar considerations to the 'Data' instance:
+-- it preserves abstraction at the cost of inefficiency.
+--
+-- @since 1.2.4.0
+instance Lift Text where
+  lift = TH.appE (TH.varE 'pack) . TH.stringE . unpack
+
 #if MIN_VERSION_base(4,7,0)
 -- | Only defined for @base-4.7.0.0@ and later
 --
@@ -793,7 +810,7 @@
 -- itself.
 toCaseFold :: Text -> Text
 toCaseFold t = unstream (S.toCaseFold (stream t))
-{-# INLINE [0] toCaseFold #-}
+{-# INLINE toCaseFold #-}
 
 -- | /O(n)/ Convert a string to lower case, using simple case
 -- conversion.  Subject to fusion.
@@ -936,8 +953,7 @@
 {-# INLINE scanl #-}
 
 -- | /O(n)/ 'scanl1' is a variant of 'scanl' that has no starting
--- value argument.  Subject to fusion.  Performs replacement on
--- invalid scalar values.
+-- value argument.  Performs replacement on invalid scalar values.
 --
 -- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]
 scanl1 :: (Char -> Char -> Char) -> Text -> Text
@@ -1034,6 +1050,8 @@
 {-# RULES
 "LAZY TEXT replicate/singleton -> replicateChar" [~1] forall n c.
     replicate n (singleton c) = replicateChar n c
+"LAZY TEXT replicate/unstream/singleton -> replicateChar" [~1] forall n c.
+    replicate n (unstream (S.singleton c)) = replicateChar n c
   #-}
 
 -- | /O(n)/, where @n@ is the length of the result. The 'unfoldr'
@@ -1041,8 +1059,9 @@
 -- 'Text' from a seed value. The function takes the element and
 -- returns 'Nothing' if it is done producing the 'Text', otherwise
 -- 'Just' @(a,b)@.  In this case, @a@ is the next 'Char' in the
--- string, and @b@ is the seed value for further production.  Performs
--- replacement on invalid scalar values.
+-- string, and @b@ is the seed value for further production.
+-- Subject to fusion.
+-- Performs replacement on invalid scalar values.
 unfoldr :: (a -> Maybe (Char,a)) -> a -> Text
 unfoldr f s = unstream (S.unfoldr (firstf safe . f) s)
 {-# INLINE unfoldr #-}
@@ -1052,6 +1071,7 @@
 -- first argument to 'unfoldrN'. This function is more efficient than
 -- 'unfoldr' when the maximum length of the result is known and
 -- correct, otherwise its performance is similar to 'unfoldr'.
+-- Subject to fusion.
 -- Performs replacement on invalid scalar values.
 unfoldrN :: Int64 -> (a -> Maybe (Char,a)) -> a -> Text
 unfoldrN n f s = unstream (S.unfoldrN n (firstf safe . f) s)
@@ -1228,7 +1248,7 @@
 
 -- | /O(n)/ 'dropAround' @p@ @t@ returns the substring remaining after
 -- dropping characters that satisfy the predicate @p@ from both the
--- beginning and end of @t@.  Subject to fusion.
+-- beginning and end of @t@.
 dropAround :: (Char -> Bool) -> Text -> Text
 dropAround p = dropWhile p . dropWhileEnd p
 {-# INLINE [1] dropAround #-}
@@ -1238,7 +1258,7 @@
 -- > dropWhile isSpace
 stripStart :: Text -> Text
 stripStart = dropWhile isSpace
-{-# INLINE [1] stripStart #-}
+{-# INLINE stripStart #-}
 
 -- | /O(n)/ Remove trailing white space from a string.  Equivalent to:
 --
@@ -1656,7 +1676,7 @@
 
 -- | /O(n)/ The 'find' function takes a predicate and a 'Text', and
 -- returns the first element in matching the predicate, or 'Nothing'
--- if there is no such element.
+-- if there is no such element. Subject to fusion.
 find :: (Char -> Bool) -> Text -> Maybe Char
 find p t = S.findBy p (stream t)
 {-# INLINE find #-}
@@ -1671,6 +1691,7 @@
 {-# INLINE partition #-}
 
 -- | /O(n)/ 'Text' index (subscript) operator, starting from 0.
+-- Subject to fusion.
 index :: Text -> Int64 -> Char
 index t n = S.index (stream t) n
 {-# INLINE index #-}
diff --git a/Data/Text/Show.hs b/Data/Text/Show.hs
--- a/Data/Text/Show.hs
+++ b/Data/Text/Show.hs
@@ -42,8 +42,7 @@
 unpack = S.unstreamList . stream
 {-# INLINE [1] unpack #-}
 
--- | /O(n)/ Convert a literal string into a 'Text'.  Subject to
--- fusion.
+-- | /O(n)/ Convert a literal string into a 'Text'.
 --
 -- This is exposed solely for people writing GHC rewrite rules.
 --
diff --git a/benchmarks/haskell/Benchmarks.hs b/benchmarks/haskell/Benchmarks.hs
--- a/benchmarks/haskell/Benchmarks.hs
+++ b/benchmarks/haskell/Benchmarks.hs
@@ -5,7 +5,7 @@
     ( main
     ) where
 
-import Criterion.Main (Benchmark, defaultMain, bgroup)
+import Criterion.Main (defaultMain, bgroup, env)
 import System.FilePath ((</>))
 import System.IO (IOMode (WriteMode), openFile, hSetEncoding, utf8)
 
@@ -32,50 +32,42 @@
 import qualified Benchmarks.Programs.Throughput as Programs.Throughput
 
 main :: IO ()
-main = benchmarks >>= defaultMain
-
-benchmarks :: IO [Benchmark]
-benchmarks = do
+main = do
     sink <- openFile "/dev/null" WriteMode
     hSetEncoding sink utf8
-
-    -- Traditional benchmarks
-    bs <- sequence
+    defaultMain
         [ Builder.benchmark
         , Concat.benchmark
-        , DecodeUtf8.benchmark "html" (tf "libya-chinese.html")
-        , DecodeUtf8.benchmark "xml" (tf "yiwiki.xml")
-        , DecodeUtf8.benchmark "ascii" (tf "ascii.txt")
-        , DecodeUtf8.benchmark "russian" (tf "russian.txt")
-        , DecodeUtf8.benchmark "japanese" (tf "japanese.txt")
+        , env (DecodeUtf8.initEnv (tf "libya-chinese.html")) (DecodeUtf8.benchmark "html")
+        , env (DecodeUtf8.initEnv (tf "yiwiki.xml")) (DecodeUtf8.benchmark "xml")
+        , env (DecodeUtf8.initEnv (tf "ascii.txt")) (DecodeUtf8.benchmark "ascii")
+        , env (DecodeUtf8.initEnv (tf "russian.txt")) (DecodeUtf8.benchmark  "russian")
+        , env (DecodeUtf8.initEnv (tf "japanese.txt")) (DecodeUtf8.benchmark "japanese")
         , EncodeUtf8.benchmark "επανάληψη 竺法蘭共譯"
-        , Equality.benchmark (tf "japanese.txt")
+        , env (Equality.initEnv (tf "japanese.txt")) Equality.benchmark
         , FileRead.benchmark (tf "russian.txt")
         , FoldLines.benchmark (tf "russian.txt")
-        , Mul.benchmark
-        , Pure.benchmark "tiny" (tf "tiny.txt")
-        , Pure.benchmark "ascii" (tf "ascii-small.txt")
-        -- , Pure.benchmark "france" (tf "france.html")
-        , Pure.benchmark "russian" (tf "russian-small.txt")
-        , Pure.benchmark "japanese" (tf "japanese.txt")
-        , ReadNumbers.benchmark (tf "numbers.txt")
-        , Replace.benchmark (tf "russian.txt") "принимая" "своем"
-        , Search.benchmark (tf "russian.txt") "принимая"
-        , Stream.benchmark (tf "russian.txt")
-        , WordFrequencies.benchmark (tf "russian.txt")
-        ]
-
-    -- Program-like benchmarks
-    ps <- bgroup "Programs" `fmap` sequence
-        [ Programs.BigTable.benchmark sink
-        , Programs.Cut.benchmark (tf "russian.txt") sink 20 40
-        , Programs.Fold.benchmark (tf "russian.txt") sink
-        , Programs.Sort.benchmark (tf "russian.txt") sink
-        , Programs.StripTags.benchmark (tf "yiwiki.xml") sink
-        , Programs.Throughput.benchmark (tf "russian.txt") sink
+        , env Mul.initEnv Mul.benchmark
+        , env (Pure.initEnv (tf "tiny.txt")) (Pure.benchmark "tiny")
+        , env (Pure.initEnv (tf "ascii-small.txt")) (Pure.benchmark "ascii-small")
+        , env (Pure.initEnv (tf "ascii.txt")) (Pure.benchmark "ascii")
+        , env (Pure.initEnv (tf "english.txt")) (Pure.benchmark "english")
+        , env (Pure.initEnv (tf "russian-small.txt")) (Pure.benchmark "russian")
+        , env (Pure.initEnv (tf "japanese.txt")) (Pure.benchmark "japanese")
+        , env (ReadNumbers.initEnv (tf "numbers.txt")) ReadNumbers.benchmark
+        , env (Replace.initEnv (tf "russian.txt")) (Replace.benchmark "принимая" "своем")
+        , env (Search.initEnv (tf "russian.txt")) (Search.benchmark "принимая")
+        , env (Stream.initEnv (tf "russian.txt")) Stream.benchmark
+        , env (WordFrequencies.initEnv (tf "russian.txt")) WordFrequencies.benchmark
+        , bgroup "Programs"
+            [ Programs.BigTable.benchmark sink
+            , Programs.Cut.benchmark (tf "russian.txt") sink 20 40
+            , Programs.Fold.benchmark (tf "russian.txt") sink
+            , Programs.Sort.benchmark (tf "russian.txt") sink
+            , Programs.StripTags.benchmark (tf "yiwiki.xml") sink
+            , Programs.Throughput.benchmark (tf "russian.txt") sink
+            ]
         ]
-
-    return $ bs ++ [ps]
-  where
+    where
     -- Location of a test file
     tf = ("../tests/text-test-data" </>)
diff --git a/benchmarks/haskell/Benchmarks/Builder.hs b/benchmarks/haskell/Benchmarks/Builder.hs
--- a/benchmarks/haskell/Benchmarks/Builder.hs
+++ b/benchmarks/haskell/Benchmarks/Builder.hs
@@ -23,8 +23,8 @@
 import qualified Data.Text.Lazy.Builder.Int as Int
 import Data.Int (Int64)
 
-benchmark :: IO Benchmark
-benchmark = return $ bgroup "Builder"
+benchmark :: Benchmark
+benchmark = bgroup "Builder"
     [ bgroup "Comparison"
       [ bench "LazyText" $ nf
           (LT.length . LTB.toLazyText . mconcat . map LTB.fromText) texts
diff --git a/benchmarks/haskell/Benchmarks/Concat.hs b/benchmarks/haskell/Benchmarks/Concat.hs
--- a/benchmarks/haskell/Benchmarks/Concat.hs
+++ b/benchmarks/haskell/Benchmarks/Concat.hs
@@ -6,8 +6,8 @@
 import Criterion (Benchmark, bgroup, bench, whnf)
 import Data.Text as T
 
-benchmark :: IO Benchmark
-benchmark = return $ bgroup "Concat"
+benchmark :: Benchmark
+benchmark = bgroup "Concat"
   [ bench "append" $ whnf (append4 "Text 1" "Text 2" "Text 3") "Text 4"
   , bench "concat" $ whnf (concat4 "Text 1" "Text 2" "Text 3") "Text 4"
   , bench "write"  $ whnf (write4  "Text 1" "Text 2" "Text 3") "Text 4"
diff --git a/benchmarks/haskell/Benchmarks/DecodeUtf8.hs b/benchmarks/haskell/Benchmarks/DecodeUtf8.hs
--- a/benchmarks/haskell/Benchmarks/DecodeUtf8.hs
+++ b/benchmarks/haskell/Benchmarks/DecodeUtf8.hs
@@ -15,7 +15,8 @@
 -- The latter are used for testing stream fusion.
 --
 module Benchmarks.DecodeUtf8
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Foreign.C.Types
@@ -34,10 +35,16 @@
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.Encoding as TL
 
-benchmark :: String -> FilePath -> IO Benchmark
-benchmark kind fp = do
+type Env = (B.ByteString, BL.ByteString)
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
     bs  <- B.readFile fp
     lbs <- BL.readFile fp
+    return (bs, lbs)
+
+benchmark :: String -> Env -> Benchmark
+benchmark kind ~(bs, lbs) =
     let bench name = C.bench (name ++ "+" ++ kind)
         decodeStream (Chunk b0 bs0) = case T.streamDecodeUtf8 b0 of
                                         T.Some t0 _ f0 -> t0 : go f0 bs0
@@ -45,7 +52,7 @@
                                        T.Some t1 _ f1 -> t1 : go f1 bs1
                 go _ _ = []
         decodeStream _ = []
-    return $ bgroup "DecodeUtf8"
+    in bgroup "DecodeUtf8"
         [ bench "Strict" $ nf T.decodeUtf8 bs
         , bench "Stream" $ nf decodeStream lbs
         , bench "IConv" $ whnfIO $ iconv bs
diff --git a/benchmarks/haskell/Benchmarks/EncodeUtf8.hs b/benchmarks/haskell/Benchmarks/EncodeUtf8.hs
--- a/benchmarks/haskell/Benchmarks/EncodeUtf8.hs
+++ b/benchmarks/haskell/Benchmarks/EncodeUtf8.hs
@@ -18,9 +18,9 @@
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.Encoding as TL
 
-benchmark :: String -> IO Benchmark
-benchmark string = do
-    return $ bgroup "EncodeUtf8"
+benchmark :: String -> Benchmark
+benchmark string =
+    bgroup "EncodeUtf8"
         [ bench "Text"     $ whnf (B.length . T.encodeUtf8)   text
         , bench "LazyText" $ whnf (BL.length . TL.encodeUtf8) lazyText
         ]
diff --git a/benchmarks/haskell/Benchmarks/Equality.hs b/benchmarks/haskell/Benchmarks/Equality.hs
--- a/benchmarks/haskell/Benchmarks/Equality.hs
+++ b/benchmarks/haskell/Benchmarks/Equality.hs
@@ -6,7 +6,8 @@
 -- * Comparison of strings (Eq instance)
 --
 module Benchmarks.Equality
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Criterion (Benchmark, bgroup, bench, whnf)
@@ -17,8 +18,10 @@
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.Encoding as TL
 
-benchmark :: FilePath -> IO Benchmark
-benchmark fp = do
+type Env = (T.Text, TL.Text, B.ByteString, BL.ByteString, BL.ByteString, String)
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
   b <- B.readFile fp
   bl1 <- BL.readFile fp
   -- A lazy bytestring is a list of chunks. When we do not explicitly create two
@@ -27,9 +30,11 @@
   -- we read the lazy bytestring twice here.
   bl2 <- BL.readFile fp
   l <- readFile fp
-  let t  = T.decodeUtf8 b
-      tl = TL.decodeUtf8 bl1
-  return $ bgroup "Equality"
+  return (T.decodeUtf8 b, TL.decodeUtf8 bl1, b, bl1, bl2, l)
+
+benchmark :: Env -> Benchmark
+benchmark ~(t, tl, b, bl1, bl2, l) =
+  bgroup "Equality"
     [ bench "Text" $ whnf (== T.init t `T.snoc` '\xfffd') t
     , bench "LazyText" $ whnf (== TL.init tl `TL.snoc` '\xfffd') tl
     , bench "ByteString" $ whnf (== B.init b `B.snoc` '\xfffd') b
diff --git a/benchmarks/haskell/Benchmarks/FileRead.hs b/benchmarks/haskell/Benchmarks/FileRead.hs
--- a/benchmarks/haskell/Benchmarks/FileRead.hs
+++ b/benchmarks/haskell/Benchmarks/FileRead.hs
@@ -19,8 +19,8 @@
 import qualified Data.Text.Lazy.Encoding as LT
 import qualified Data.Text.Lazy.IO as LT
 
-benchmark :: FilePath -> IO Benchmark
-benchmark p = return $ bgroup "FileRead"
+benchmark :: FilePath -> Benchmark
+benchmark p = bgroup "FileRead"
     [ bench "String" $ whnfIO $ length <$> readFile p
     , bench "ByteString" $ whnfIO $ SB.length <$> SB.readFile p
     , bench "LazyByteString" $ whnfIO $ LB.length <$> LB.readFile p
diff --git a/benchmarks/haskell/Benchmarks/FoldLines.hs b/benchmarks/haskell/Benchmarks/FoldLines.hs
--- a/benchmarks/haskell/Benchmarks/FoldLines.hs
+++ b/benchmarks/haskell/Benchmarks/FoldLines.hs
@@ -16,8 +16,8 @@
 import qualified Data.Text as T
 import qualified Data.Text.IO as T
 
-benchmark :: FilePath -> IO Benchmark
-benchmark fp = return $ bgroup "ReadLines"
+benchmark :: FilePath -> Benchmark
+benchmark fp = bgroup "ReadLines"
     [ bench "Text"       $ withHandle $ foldLinesT (\n _ -> n + 1) (0 :: Int)
     , bench "ByteString" $ withHandle $ foldLinesB (\n _ -> n + 1) (0 :: Int)
     ]
diff --git a/benchmarks/haskell/Benchmarks/Mul.hs b/benchmarks/haskell/Benchmarks/Mul.hs
--- a/benchmarks/haskell/Benchmarks/Mul.hs
+++ b/benchmarks/haskell/Benchmarks/Mul.hs
@@ -1,4 +1,7 @@
-module Benchmarks.Mul (benchmark) where
+module Benchmarks.Mul
+    ( initEnv
+    , benchmark
+    ) where
 
 import Control.Exception (evaluate)
 import Criterion.Main
@@ -12,16 +15,21 @@
     | m <= maxBound `quot` n = m * n
     | otherwise              = error "overflow"
 
-benchmark :: IO Benchmark
-benchmark = do
-  _ <- evaluate testVector32
-  _ <- evaluate testVector64
-  return $ bgroup "Mul" [
-      bench "oldMul" $ whnf (U.map (uncurry oldMul)) testVector64
-    , bench "mul64" $ whnf (U.map (uncurry mul64)) testVector64
-    , bench "*64" $ whnf (U.map (uncurry (*))) testVector64
-    , bench "mul32" $ whnf (U.map (uncurry mul32)) testVector32
-    , bench "*32" $ whnf (U.map (uncurry (*))) testVector32
+type Env = (U.Vector (Int32,Int32), U.Vector (Int64,Int64))
+
+initEnv :: IO Env
+initEnv = do
+    x <- evaluate testVector32
+    y <- evaluate testVector64
+    return (x, y)
+
+benchmark :: Env -> Benchmark
+benchmark ~(tv32, tv64) = bgroup "Mul"
+    [ bench "oldMul" $ whnf (U.map (uncurry oldMul)) tv64
+    , bench "mul64" $ whnf (U.map (uncurry mul64)) tv64
+    , bench "*64" $ whnf (U.map (uncurry (*))) tv64
+    , bench "mul32" $ whnf (U.map (uncurry mul32)) tv32
+    , bench "*32" $ whnf (U.map (uncurry (*))) tv32
     ]
 
 testVector64 :: U.Vector (Int64,Int64)
diff --git a/benchmarks/haskell/Benchmarks/Programs/BigTable.hs b/benchmarks/haskell/Benchmarks/Programs/BigTable.hs
--- a/benchmarks/haskell/Benchmarks/Programs/BigTable.hs
+++ b/benchmarks/haskell/Benchmarks/Programs/BigTable.hs
@@ -18,8 +18,8 @@
 import System.IO (Handle)
 import qualified Data.Text as T
 
-benchmark :: Handle -> IO Benchmark
-benchmark sink = return $ bench "BigTable" $ whnfIO $ do
+benchmark :: Handle -> Benchmark
+benchmark sink = bench "BigTable" $ whnfIO $ do
     hPutStr sink "Content-Type: text/html\n\n<table>"
     hPutStr sink . toLazyText . makeTable =<< rows
     hPutStr sink "</table>"
diff --git a/benchmarks/haskell/Benchmarks/Programs/Cut.hs b/benchmarks/haskell/Benchmarks/Programs/Cut.hs
--- a/benchmarks/haskell/Benchmarks/Programs/Cut.hs
+++ b/benchmarks/haskell/Benchmarks/Programs/Cut.hs
@@ -29,8 +29,8 @@
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Data.Text.Lazy.IO as TL
 
-benchmark :: FilePath -> Handle -> Int -> Int -> IO Benchmark
-benchmark p sink from to = return $ bgroup "Cut"
+benchmark :: FilePath -> Handle -> Int -> Int -> Benchmark
+benchmark p sink from to = bgroup "Cut"
     [ bench' "String" string
     , bench' "ByteString" byteString
     , bench' "LazyByteString" lazyByteString
diff --git a/benchmarks/haskell/Benchmarks/Programs/Fold.hs b/benchmarks/haskell/Benchmarks/Programs/Fold.hs
--- a/benchmarks/haskell/Benchmarks/Programs/Fold.hs
+++ b/benchmarks/haskell/Benchmarks/Programs/Fold.hs
@@ -28,8 +28,8 @@
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.IO as TL
 
-benchmark :: FilePath -> Handle -> IO Benchmark
-benchmark i o = return $
+benchmark :: FilePath -> Handle -> Benchmark
+benchmark i o =
     bench "Fold" $ whnfIO $ T.readFile i >>= TL.hPutStr o . fold 80
 
 -- | We represent a paragraph by a word list
diff --git a/benchmarks/haskell/Benchmarks/Programs/Sort.hs b/benchmarks/haskell/Benchmarks/Programs/Sort.hs
--- a/benchmarks/haskell/Benchmarks/Programs/Sort.hs
+++ b/benchmarks/haskell/Benchmarks/Programs/Sort.hs
@@ -33,8 +33,8 @@
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Data.Text.Lazy.IO as TL
 
-benchmark :: FilePath -> Handle -> IO Benchmark
-benchmark i o = return $ bgroup "Sort"
+benchmark :: FilePath -> Handle -> Benchmark
+benchmark i o = bgroup "Sort"
     [ bench "String" $ whnfIO $ readFile i >>= hPutStr o . string
     , bench "ByteString" $ whnfIO $ B.readFile i >>= B.hPutStr o . byteString
     , bench "LazyByteString" $ whnfIO $
diff --git a/benchmarks/haskell/Benchmarks/Programs/StripTags.hs b/benchmarks/haskell/Benchmarks/Programs/StripTags.hs
--- a/benchmarks/haskell/Benchmarks/Programs/StripTags.hs
+++ b/benchmarks/haskell/Benchmarks/Programs/StripTags.hs
@@ -24,8 +24,8 @@
 import qualified Data.Text.Encoding as T
 import qualified Data.Text.IO as T
 
-benchmark :: FilePath -> Handle -> IO Benchmark
-benchmark i o = return $ bgroup "StripTags"
+benchmark :: FilePath -> Handle -> Benchmark
+benchmark i o = bgroup "StripTags"
     [ bench "String" $ whnfIO $ readFile i >>= hPutStr o . string
     , bench "ByteString" $ whnfIO $ B.readFile i >>= B.hPutStr o . byteString
     , bench "Text" $ whnfIO $ T.readFile i >>= T.hPutStr o . text
diff --git a/benchmarks/haskell/Benchmarks/Programs/Throughput.hs b/benchmarks/haskell/Benchmarks/Programs/Throughput.hs
--- a/benchmarks/haskell/Benchmarks/Programs/Throughput.hs
+++ b/benchmarks/haskell/Benchmarks/Programs/Throughput.hs
@@ -27,8 +27,8 @@
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Data.Text.Lazy.IO as TL
 
-benchmark :: FilePath -> Handle -> IO Benchmark
-benchmark fp sink = return $ bgroup "Throughput"
+benchmark :: FilePath -> Handle -> Benchmark
+benchmark fp sink = bgroup "Throughput"
     [ bench "String" $ whnfIO $ readFile fp >>= hPutStr sink
     , bench "ByteString" $ whnfIO $ B.readFile fp >>= B.hPutStr sink
     , bench "LazyByteString" $ whnfIO $ BL.readFile fp >>= BL.hPutStr sink
diff --git a/benchmarks/haskell/Benchmarks/Pure.hs b/benchmarks/haskell/Benchmarks/Pure.hs
--- a/benchmarks/haskell/Benchmarks/Pure.hs
+++ b/benchmarks/haskell/Benchmarks/Pure.hs
@@ -5,9 +5,11 @@
 -- * Most pure functions defined the string types
 --
 {-# LANGUAGE BangPatterns, CPP, GADTs, MagicHash #-}
+{-# LANGUAGE DeriveAnyClass, DeriveGeneric, RecordWildCards #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Benchmarks.Pure
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Control.DeepSeq (NFData (..))
@@ -16,6 +18,8 @@
 import Data.Char (toLower, toUpper)
 import Data.Monoid (mappend, mempty)
 import GHC.Base (Char (..), Int (..), chr#, ord#, (+#))
+import GHC.Generics (Generic)
+import GHC.Int (Int64)
 import qualified Data.ByteString.Char8 as BS
 import qualified Data.ByteString.Lazy.Char8 as BL
 import qualified Data.ByteString.UTF8 as UTF8
@@ -26,8 +30,32 @@
 import qualified Data.Text.Lazy.Builder as TB
 import qualified Data.Text.Lazy.Encoding as TL
 
-benchmark :: String -> FilePath -> IO Benchmark
-benchmark kind fp = do
+data Env = Env
+    { bsa :: !BS.ByteString
+    , ta :: !T.Text
+    , tb :: !T.Text
+    , tla :: !TL.Text
+    , tlb :: !TL.Text
+    , bsb :: !BS.ByteString
+    , bla :: !BL.ByteString
+    , blb :: !BL.ByteString
+    , sa :: !String
+    , sb :: !String
+    , bsa_len :: !Int
+    , ta_len :: !Int
+    , bla_len :: !Int64
+    , tla_len :: !Int64
+    , sa_len :: !Int
+    , bsl :: [BS.ByteString]
+    , bll :: [BL.ByteString]
+    , tl :: [T.Text]
+    , tll :: [TL.Text]
+    , sl :: [String]
+    } deriving (Generic, NFData)
+
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
     -- Evaluate stuff before actually running the benchmark, we don't want to
     -- count it here.
 
@@ -63,7 +91,11 @@
     tll     <- evaluate $ TL.lines tla
     sl      <- evaluate $ L.lines sa
 
-    return $ bgroup "Pure"
+    return Env{..}
+
+benchmark :: String -> Env -> Benchmark
+benchmark kind ~Env{..} =
+    bgroup "Pure"
         [ bgroup "append"
             [ benchT   $ nf (T.append tb) ta
             , benchTL  $ nf (TL.append tlb) tla
diff --git a/benchmarks/haskell/Benchmarks/ReadNumbers.hs b/benchmarks/haskell/Benchmarks/ReadNumbers.hs
--- a/benchmarks/haskell/Benchmarks/ReadNumbers.hs
+++ b/benchmarks/haskell/Benchmarks/ReadNumbers.hs
@@ -17,7 +17,8 @@
 -- * Lexing/parsing of different numerical types
 --
 module Benchmarks.ReadNumbers
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Criterion (Benchmark, bgroup, bench, whnf)
@@ -33,8 +34,10 @@
 import qualified Data.Text.Lazy.Read as TL
 import qualified Data.Text.Read as T
 
-benchmark :: FilePath -> IO Benchmark
-benchmark fp = do
+type Env = ([String], [T.Text], [TL.Text], [B.ByteString], [BL.ByteString])
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
     -- Read all files into lines: string, text, lazy text, bytestring, lazy
     -- bytestring
     s <- lines `fmap` readFile fp
@@ -42,7 +45,11 @@
     tl <- TL.lines `fmap` TL.readFile fp
     b <- B.lines `fmap` B.readFile fp
     bl <- BL.lines `fmap` BL.readFile fp
-    return $ bgroup "ReadNumbers"
+    return (s, t, tl, b, bl)
+
+benchmark :: Env -> Benchmark
+benchmark ~(s, t, tl, b, bl) =
+    bgroup "ReadNumbers"
         [ bench "DecimalString"     $ whnf (int . string readDec) s
         , bench "HexadecimalString" $ whnf (int . string readHex) s
         , bench "DoubleString"      $ whnf (double . string readFloat) s
diff --git a/benchmarks/haskell/Benchmarks/Replace.hs b/benchmarks/haskell/Benchmarks/Replace.hs
--- a/benchmarks/haskell/Benchmarks/Replace.hs
+++ b/benchmarks/haskell/Benchmarks/Replace.hs
@@ -7,6 +7,7 @@
 --
 module Benchmarks.Replace
     ( benchmark
+    , initEnv
     ) where
 
 import Criterion (Benchmark, bgroup, bench, nf)
@@ -20,13 +21,19 @@
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Data.Text.Lazy.IO as TL
 
-benchmark :: FilePath -> String -> String -> IO Benchmark
-benchmark fp pat sub = do
+type Env = (T.Text, B.ByteString, TL.Text, BL.ByteString)
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
     tl <- TL.readFile fp
     bl <- BL.readFile fp
     let !t = TL.toStrict tl
         !b = T.encodeUtf8 t
-    return $ bgroup "Replace" [
+    return (t, b, tl, bl)
+
+benchmark :: String -> String -> Env -> Benchmark
+benchmark pat sub ~(t, b, tl, bl) =
+    bgroup "Replace" [
           bench "Text"           $ nf (T.length . T.replace tpat tsub) t
         , bench "ByteString"     $ nf (BL.length . B.replace bpat bsub) b
         , bench "LazyText"       $ nf (TL.length . TL.replace tlpat tlsub) tl
diff --git a/benchmarks/haskell/Benchmarks/Search.hs b/benchmarks/haskell/Benchmarks/Search.hs
--- a/benchmarks/haskell/Benchmarks/Search.hs
+++ b/benchmarks/haskell/Benchmarks/Search.hs
@@ -5,7 +5,8 @@
 -- * Searching all occurences of a pattern using library routines
 --
 module Benchmarks.Search
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Criterion (Benchmark, bench, bgroup, whnf)
@@ -19,13 +20,19 @@
 import qualified Data.Text.Lazy as TL
 import qualified Data.Text.Lazy.IO as TL
 
-benchmark :: FilePath -> T.Text -> IO Benchmark
-benchmark fp needleT = do
+type Env = (B.ByteString, BL.ByteString, T.Text, TL.Text)
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
     b  <- B.readFile fp
     bl <- BL.readFile fp
     t  <- T.readFile fp
     tl <- TL.readFile fp
-    return $ bgroup "FileIndices"
+    return (b, bl, t, tl)
+
+benchmark :: T.Text -> Env -> Benchmark
+benchmark needleT ~(b, bl, t, tl) =
+    bgroup "FileIndices"
         [ bench "ByteString"     $ whnf (byteString needleB)     b
         , bench "LazyByteString" $ whnf (lazyByteString needleB) bl
         , bench "Text"           $ whnf (text needleT)           t
diff --git a/benchmarks/haskell/Benchmarks/Stream.hs b/benchmarks/haskell/Benchmarks/Stream.hs
--- a/benchmarks/haskell/Benchmarks/Stream.hs
+++ b/benchmarks/haskell/Benchmarks/Stream.hs
@@ -6,24 +6,31 @@
 -- * Most streaming functions
 --
 {-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveAnyClass, DeriveGeneric, RecordWildCards #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Benchmarks.Stream
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Control.DeepSeq (NFData (..))
 import Criterion (Benchmark, bgroup, bench, nf)
+import qualified Data.Text as T
+import qualified Data.ByteString as B
+import qualified Data.Text.Lazy as TL
+import qualified Data.ByteString.Lazy as BL
 import Data.Text.Internal.Fusion.Types (Step (..), Stream (..))
 import qualified Data.Text.Encoding as T
 import qualified Data.Text.Encoding.Error as E
 import qualified Data.Text.Internal.Encoding.Fusion as T
 import qualified Data.Text.Internal.Encoding.Fusion.Common as F
-import qualified Data.Text.Internal.Fusion as F
+import qualified Data.Text.Internal.Fusion as T
 import qualified Data.Text.IO as T
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Data.Text.Internal.Lazy.Encoding.Fusion as TL
-import qualified Data.Text.Internal.Lazy.Fusion as FL
+import qualified Data.Text.Internal.Lazy.Fusion as TL
 import qualified Data.Text.Lazy.IO as TL
+import GHC.Generics (Generic)
 
 instance NFData a => NFData (Stream a) where
     -- Currently, this implementation does not force evaluation of the size hint
@@ -34,8 +41,24 @@
             Skip s'    -> go s'
             Yield x s' -> rnf x `seq` go s'
 
-benchmark :: FilePath -> IO Benchmark
-benchmark fp = do
+data Env = Env
+    { t :: !T.Text
+    , utf8 :: !B.ByteString
+    , utf16le :: !B.ByteString
+    , utf16be :: !B.ByteString
+    , utf32le :: !B.ByteString
+    , utf32be :: !B.ByteString
+    , tl :: !TL.Text
+    , utf8L :: !BL.ByteString
+    , utf16leL :: !BL.ByteString
+    , utf16beL :: !BL.ByteString
+    , utf32leL :: !BL.ByteString
+    , utf32beL :: !BL.ByteString
+    , s :: T.Stream Char
+    } deriving (Generic, NFData)
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do 
     -- Different formats
     t  <- T.readFile fp
     let !utf8    = T.encodeUtf8 t
@@ -53,25 +76,16 @@
         !utf32beL = TL.encodeUtf32BE tl
 
     -- For the functions which operate on streams
-    let !s = F.stream t
-
-    return $ bgroup "Stream"
+    let !s = T.stream t
+    return Env{..}
 
+benchmark :: Env -> Benchmark
+benchmark ~Env{..} =
+    bgroup "Stream"
         -- Fusion
         [ bgroup "stream" $
-            [ bench "Text"     $ nf F.stream t
-            , bench "LazyText" $ nf FL.stream tl
-            ]
-          -- must perform exactly the same as stream above due to
-          -- stream/unstream (i.e. stream after unstream) fusion
-        , bgroup "stream-fusion" $
-            [ bench "Text"     $ nf (F.stream . F.unstream . F.stream) t
-            , bench "LazyText" $ nf (FL.stream . FL.unstream . FL.stream) tl
-            ]
-          -- measure the overhead of unstream after stream
-        , bgroup "stream-unstream" $
-            [ bench "Text"     $ nf (F.unstream . F.stream) t
-            , bench "LazyText" $ nf (FL.unstream . FL.stream) tl
+            [ bench "Text"     $ nf T.stream t
+            , bench "LazyText" $ nf TL.stream tl
             ]
 
         -- Encoding.Fusion
diff --git a/benchmarks/haskell/Benchmarks/WordFrequencies.hs b/benchmarks/haskell/Benchmarks/WordFrequencies.hs
--- a/benchmarks/haskell/Benchmarks/WordFrequencies.hs
+++ b/benchmarks/haskell/Benchmarks/WordFrequencies.hs
@@ -9,7 +9,8 @@
 -- * Comparing: Eq/Ord instances
 --
 module Benchmarks.WordFrequencies
-    ( benchmark
+    ( initEnv
+    , benchmark
     ) where
 
 import Criterion (Benchmark, bench, bgroup, whnf)
@@ -21,12 +22,18 @@
 import qualified Data.Text as T
 import qualified Data.Text.IO as T
 
-benchmark :: FilePath -> IO Benchmark
-benchmark fp = do
+type Env = (String, B.ByteString, T.Text)
+
+initEnv :: FilePath -> IO Env
+initEnv fp = do
     s <- readFile fp
     b <- B.readFile fp
     t <- T.readFile fp
-    return $ bgroup "WordFrequencies"
+    return (s, b, t)
+
+benchmark :: Env -> Benchmark
+benchmark ~(s, b, t) =
+    bgroup "WordFrequencies"
         [ bench "String"     $ whnf (frequencies . words . map toLower)     s
         , bench "ByteString" $ whnf (frequencies . B.words . B.map toLower) b
         , bench "Text"       $ whnf (frequencies . T.words . T.toLower)     t
diff --git a/benchmarks/text-benchmarks.cabal b/benchmarks/text-benchmarks.cabal
--- a/benchmarks/text-benchmarks.cabal
+++ b/benchmarks/text-benchmarks.cabal
@@ -1,3 +1,4 @@
+cabal-version:       1.12
 name:                text-benchmarks
 version:             0.0.0.0
 synopsis:            Benchmarks for the text package
@@ -12,7 +13,6 @@
 maintainer:          jaspervdj@gmail.com
 category:            Text
 build-type:          Simple
-cabal-version:       >=1.8
 
 flag bytestring-builder
   description: Depend on the bytestring-builder package for backwards compatibility.
@@ -42,6 +42,7 @@
                   ghc-prim,
                   integer-gmp,
                   stringsearch,
+                  template-haskell,
                   transformers,
                   utf8-string,
                   vector
@@ -129,6 +130,10 @@
     Data.Text.Unsafe
     Data.Text.Show
 
+  default-language: Haskell2010
+  default-extensions: NondecreasingIndentation
+
+
 executable text-multilang
   hs-source-dirs: haskell
   main-is:        Multilang.hs
@@ -138,3 +143,6 @@
                   bytestring,
                   text,
                   time
+
+  default-language: Haskell2010
+  default-extensions: NondecreasingIndentation
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,8 +1,15 @@
-#### 1.2.3.2
+### 1.2.4.0
 
-* Special release supporting GHC 8.10.1 / `base-4.14.0.0` only
+* Add TH `Lift` instances for `Data.Text.Text` and `Data.Text.Lazy.Text` (gh-232)
 
-#### 1.2.3.1
+* Update Haddock documentation to better reflect fusion eligibility; improve fusion
+  rules for `takeWhileEnd` and `length` (gh-241, ghc-202)
+
+* Optimise `Data.Text.replicate` from `O(n)` to `O(log n)` (gh-209)
+
+* Support `base-4.13.0.0`
+
+### 1.2.3.1
 
 * Make `decodeUtf8With` fail explicitly for unsupported non-BMP
   replacement characters instead silent undefined behaviour (gh-213)
diff --git a/tests/text-tests.cabal b/tests/text-tests.cabal
--- a/tests/text-tests.cabal
+++ b/tests/text-tests.cabal
@@ -146,7 +146,8 @@
     binary,
     deepseq,
     ghc-prim,
-    integer-gmp
+    integer-gmp,
+    template-haskell
 
   if flag(bytestring-builder)
     build-depends: bytestring         >= 0.9    && < 0.10.4,
diff --git a/text.cabal b/text.cabal
--- a/text.cabal
+++ b/text.cabal
@@ -1,6 +1,6 @@
-cabal-version:  1.12
+cabal-version:  >= 1.10
 name:           text
-version:        1.2.3.2
+version:        1.2.4.0
 
 homepage:       https://github.com/haskell/text
 bug-reports:    https://github.com/haskell/text/issues
@@ -29,13 +29,26 @@
     .
     > import qualified Data.Text as T
     .
+    == ICU Support
+    .
     To use an extended and very rich family of functions for working
     with Unicode text (including normalization, regular expressions,
     non-standard encodings, text breaking, and locales), see
     the [text-icu package](https://hackage.haskell.org/package/text-icu)
     based on the well-respected and liberally
     licensed [ICU library](http://site.icu-project.org/).
+    .
+    == Internal Representation: UTF-16 vs. UTF-8
+    .
+    Currently the @text@ library uses UTF-16 as its internal representation
+    which is [neither a fixed-width nor always the most dense representation](http://utf8everywhere.org/)
+    for Unicode text. We're currently investigating the feasibility
+    of [changing Text's internal representation to UTF-8](https://github.com/text-utf8)
+    and if you need such a 'Text' type right now you might be interested in using the spin-off
+    packages <https://hackage.haskell.org/package/text-utf8 text-utf8> and
+    <https://hackage.haskell.org/package/text-short text-short>.
 
+
 license:        BSD2
 license-file:   LICENSE
 author:         Bryan O'Sullivan <bos@serpentine.com>
@@ -43,7 +56,9 @@
 copyright:      2009-2011 Bryan O'Sullivan, 2008-2009 Tom Harper
 category:       Data, Text
 build-type:     Simple
-tested-with:    GHC==8.10.1
+tested-with:    GHC==8.6.5, GHC==8.4.4,
+                GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4,
+                GHC==7.6.3, GHC==7.4.2, GHC==7.2.2, GHC==7.0.4
 extra-source-files:
     -- scripts/CaseFolding.txt
     -- scripts/SpecialCasing.txt
@@ -69,6 +84,13 @@
     tests/scripts/*.sh
     tests/text-tests.cabal
 
+flag bytestring-builder
+  description:
+    Depend on the [bytestring-builder](https://hackage.haskell.org/package/bytestring-builder)
+    package for backwards compatibility.
+  default: False
+  manual: False
+
 flag developer
   description: operate in developer mode
   default: False
@@ -134,13 +156,19 @@
     Data.Text.Show
 
   build-depends:
-    array      == 0.5.*,
-    base       == 4.14.*,
-    binary     == 0.8.*,
-    deepseq    >= 1.1.0.0 && < 1.5,
-    ghc-prim   == 0.6.*,
-    bytestring >= 0.10.4 && < 0.11
+    array            >= 0.3 && < 0.6,
+    base             >= 4.3 && < 5,
+    binary           >= 0.5 && < 0.9,
+    deepseq          >= 1.1 && < 1.5,
+    ghc-prim         >= 0.2 && < 0.6,
+    template-haskell >= 2.5 && < 2.16
 
+  if flag(bytestring-builder)
+    build-depends: bytestring         >= 0.9    && < 0.10.4,
+                   bytestring-builder >= 0.10.4.0.2 && < 0.11
+  else
+    build-depends: bytestring         >= 0.10.4 && < 0.11
+
   ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
   if flag(developer)
     ghc-prof-options: -auto-all
@@ -175,6 +203,15 @@
     UnboxedTuples
     UnliftedFFITypes
 
+  if impl(ghc >= 7.2)
+    other-extensions: Trustworthy
+  if impl(ghc >= 7.4)
+    other-extensions: Safe
+  if impl(ghc >= 8.0)
+    other-extensions: TemplateHaskellQuotes
+  else
+    other-extensions: TemplateHaskell
+
 test-suite tests
   type:           exitcode-stdio-1.0
   c-sources:      cbits/cbits.c
@@ -262,10 +299,16 @@
     ghc-prim,
     quickcheck-unicode >= 1.0.1.0,
     random,
+    template-haskell,
     test-framework >= 0.4,
     test-framework-hunit >= 0.2,
-    test-framework-quickcheck2 >= 0.2,
-    bytestring >= 0.10.4
+    test-framework-quickcheck2 >= 0.2
+
+  if flag(bytestring-builder)
+    build-depends: bytestring         >= 0.9    && < 0.10.4,
+                   bytestring-builder >= 0.10.4
+  else
+    build-depends: bytestring         >= 0.10.4
 
   if flag(integer-simple)
     cpp-options: -DINTEGER_SIMPLE
