diff --git a/README.markdown b/README.markdown
--- a/README.markdown
+++ b/README.markdown
@@ -62,7 +62,7 @@
 
 &nbsp;&nbsp;&nbsp;&nbsp;&#x2612;&nbsp;&nbsp;2017-03-16  v0.8.0.0  [Tidy up the API](https://github.com/iconnect/regex/milestone/10)
 
-&nbsp;&nbsp;&nbsp;&nbsp;&#x2610;&nbsp;&nbsp;2017-03-18  v0.9.0.0  [Add type-safe replacement templates and use TemplateHaskellQuotes](https://github.com/iconnect/regex/milestone/9)
+&nbsp;&nbsp;&nbsp;&nbsp;&#x2612;&nbsp;&nbsp;2017-03-18  v0.9.0.0  [Finish tidying up the API, Add type-safe replacement templates and exploit TemplateHaskellQuotes](https://github.com/iconnect/regex/milestone/9)
 
 &nbsp;&nbsp;&nbsp;&nbsp;&#x2610;&nbsp;&nbsp;2017-03-31  v1.0.0.0  [First stable release](https://github.com/iconnect/regex/milestone/3)
 
@@ -103,10 +103,8 @@
 
 If you have any feedback or suggestion then please drop us a line.
 
-&nbsp;&nbsp;&nbsp;&nbsp;`t` [&#64;hregex](https://twitter.com/hregex)
-
-&nbsp;&nbsp;&nbsp;&nbsp;`e` maintainers@regex.uk
-
+&nbsp;&nbsp;&nbsp;&nbsp;`t` [&#64;hregex](https://twitter.com/hregex)\n
+&nbsp;&nbsp;&nbsp;&nbsp;`e` maintainers@regex.uk\n
 &nbsp;&nbsp;&nbsp;&nbsp;`w` http://issues.regex.uk
 
 The [Contact page](http://contact.regex.uk) has more details.
diff --git a/Text/RE.hs b/Text/RE.hs
--- a/Text/RE.hs
+++ b/Text/RE.hs
@@ -18,76 +18,32 @@
   -- ** The Match Operators
   -- $operators
 
-  -- * Matches, Match & Capture
-    Matches(..)
-  , Match(..)
-  , Capture(..)
-  , noMatch
-  -- ** Matches functions
+  -- * Matches
+    Matches
+  , matchesSource
+  , allMatches
   , anyMatches
   , countMatches
   , matches
-  , mainCaptures
-  -- ** Match functions
+  -- * Match
+  , Match
+  , matchSource
   , matched
   , matchedText
-  , matchCapture
-  , matchCaptures
-  , (!$$)
-  , captureText
-  , (!$$?)
-  , captureTextMaybe
-  , (!$)
-  , capture
-  , (!$?)
-  , captureMaybe
-  -- ** Capture functions
-  , hasCaptured
-  , capturePrefix
-  , captureSuffix
-  -- * Options
-  , SimpleRegexOptions(..)
-  -- * CaptureID
-  , CaptureID
-  -- * Replace
-  , Replace(..)
-  , ReplaceMethods(..)
-  , replaceMethods
-  , Context(..)
-  , Location(..)
-  , isTopLocation
-  , replace
-  , replaceAll
-  , replaceAllCaptures
-  , replaceAllCaptures_
-  , replaceAllCapturesM
-  , replaceCaptures
-  , replaceCaptures_
-  , replaceCapturesM
-  , expandMacros
-  , expandMacros'
   ) where
 
-import           Text.RE.Types.Capture
-import           Text.RE.Types.CaptureID
 import           Text.RE.Types.Match
 import           Text.RE.Types.Matches
-import           Text.RE.Types.Options
-import           Text.RE.Types.Replace
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
+--
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
 
 -- $use
 --
--- This module won't provide any operators to match a regular expression
--- against text as it merely provides the toolkit for working with the
--- output of the match operators.  You probably won't import it directly
--- but import one of the modules that provides the match operators,
--- which will in tuen re-export this module.
---
--- The module that you choose to import will depend upon two factors:
+-- This module just provides an overview of the key type on which
+-- the regex package is built. You will need to import one of the API
+-- modules of which there is a choice which will depend upon two factors:
 --
 -- * Which flavour of regular expression do you want to use? If you want
 --   Posix flavour REs then you want the TDFA modules, otherwise its
@@ -121,9 +77,9 @@
 
 -- $operators
 --
--- The traditional @=~@ and @=~~@ operators are exported by the @regex@,
--- but we recommend that you use the two new operators, especially if
--- you are not familiar with the old operators.  We have:
+-- The traditional @=~@ and @=~~@ operators are exported by the above
+-- API module, but we recommend that you use the two new operators,
+-- especially if you are not familiar with the old operators.  We have:
 --
 -- * @txt ?=~ re@ searches for a single match yielding a value of type
 --   'Match' @a@ where @a@ is the type of the text you are searching.
@@ -131,5 +87,6 @@
 -- * @txt *=~ re@ searches for all non-overlapping matches in @txt@,
 --   returning a value of type 'Matches' @a@.
 --
--- See the sections below for more information on these @Matches@ and
--- @Match@ result types.
+-- The remainder of this module outlines these @Matches@ and
+-- @Match@ result types. Only an outline is given here. For more details
+-- see the 'Text.RE.Type.Matches' and 'Text.RE.Type.Match' modules.
diff --git a/Text/RE/Internal/AddCaptureNames.hs b/Text/RE/Internal/AddCaptureNames.hs
--- a/Text/RE/Internal/AddCaptureNames.hs
+++ b/Text/RE/Internal/AddCaptureNames.hs
@@ -16,6 +16,7 @@
 import           Prelude.Compat
 import           Text.RE
 import           Text.RE.Types.CaptureID
+import           Text.RE.Types.Match
 import           Unsafe.Coerce
 
 
diff --git a/Text/RE/Internal/EscapeREString.hs b/Text/RE/Internal/EscapeREString.hs
--- a/Text/RE/Internal/EscapeREString.hs
+++ b/Text/RE/Internal/EscapeREString.hs
@@ -1,11 +1,15 @@
 module Text.RE.Internal.EscapeREString where
 
+-- | Convert a string into a regular expression that will amtch that
+-- string
 escapeREString :: String -> String
 escapeREString = foldr esc []
   where
     esc c t | isMetaChar c = '\\' : c : t
             | otherwise    = c : t
 
+-- | returns True iff the charactr is an RE meta character
+-- ('[', '*', '{', etc.)
 isMetaChar :: Char -> Bool
 isMetaChar c = case c of
   '^'  -> True
diff --git a/Text/RE/Internal/NamedCaptures.lhs b/Text/RE/Internal/NamedCaptures.lhs
--- a/Text/RE/Internal/NamedCaptures.lhs
+++ b/Text/RE/Internal/NamedCaptures.lhs
@@ -1,13 +1,18 @@
 \begin{code}
-{-# LANGUAGE QuasiQuotes                #-}
-{-# LANGUAGE TemplateHaskell            #-}
 {-# LANGUAGE DeriveGeneric              #-}
 {-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE CPP                        #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TemplateHaskellQuotes      #-}
+#else
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE TemplateHaskell            #-}
+#endif
 
 module Text.RE.Internal.NamedCaptures
   ( cp
   , extractNamedCaptures
-  , idFormatTokenOptions
+  , idFormatTokenREOptions
   , Token(..)
   , validToken
   , formatTokens
@@ -22,23 +27,27 @@
 import           GHC.Generics
 import qualified Language.Haskell.TH          as TH
 import           Language.Haskell.TH.Quote
-import           Text.Heredoc
 import           Text.RE
 import           Text.RE.Internal.PreludeMacros
 import           Text.RE.Internal.QQ
 import           Text.RE.TestBench
 import           Text.RE.Tools.Lex
 import           Text.RE.Types.CaptureID
+import           Text.RE.Types.Match
 import           Text.Regex.TDFA
 
 
+-- | quasi quoter for CaptureID ([cp|0|],[cp|y|], etc.)
 cp :: QuasiQuoter
 cp =
     (qq0 "cp")
       { quoteExp = parse_capture
       }
 
-extractNamedCaptures :: String -> Either String (CaptureNames,String)
+-- | extract the CaptureNames from an RE or return an error diagnostic
+-- if the RE is not well formed; also returs the total number of captures
+-- in the RE
+extractNamedCaptures :: String -> Either String ((Int,CaptureNames),String)
 extractNamedCaptures s = Right (analyseTokens tks,formatTokens tks)
   where
     tks = scan s
@@ -49,6 +58,7 @@
 -----
 
 \begin{code}
+-- | our RE scanner returns a list of these tokens
 data Token
   = ECap (Maybe String)
   | PGrp
@@ -58,6 +68,7 @@
   | Other       Char
   deriving (Show,Generic,Eq)
 
+-- | check that a token is well formed
 validToken :: Token -> Bool
 validToken tkn = case tkn of
     ECap  mb -> maybe True check_ecap mb
@@ -78,11 +89,15 @@
 ---------------------------------
 
 \begin{code}
-analyseTokens :: [Token] -> CaptureNames
-analyseTokens = HM.fromList . count_em 1
+-- | analyse a token stream, returning the number of captures and the
+-- 'CaptureNames'
+analyseTokens :: [Token] -> (Int,CaptureNames)
+analyseTokens tks0 = case count_em 1 tks0 of
+    (n,as) -> (n-1, HM.fromList as)
   where
-    count_em _ []       = []
-    count_em n (tk:tks) = bd ++ count_em (n `seq` n+d) tks
+    count_em n []       = (n,[])
+    count_em n (tk:tks) = case count_em (n `seq` n+d) tks of
+        (n',as) -> (n',bd++as)
       where
         (d,bd) = case tk of
           ECap (Just nm) -> (,) 1 [(CaptureName $ T.pack nm,CaptureOrdinal n)]
@@ -99,18 +114,19 @@
 ----------------------
 
 \begin{code}
+-- | scan a RE string into a list of RE Token
 scan :: String -> [Token]
 scan = alex' match al oops
   where
     al :: [(Regex,Match String->Maybe Token)]
     al =
-      [ mk [here|\$\{([^{}]+)\}\(|] $         ECap . Just . x_1
-      , mk [here|\$\(|]             $ const $ ECap Nothing
-      , mk [here|\(\?:|]            $ const   PGrp
-      , mk [here|\(\?|]             $ const   PCap
-      , mk [here|\(|]               $ const   Bra
-      , mk [here|\\(.)|]            $         BS    . s2c . x_1
-      , mk [here|(.)|]              $         Other . s2c . x_1
+      [ mk "\\$\\{([^{}]+)\\}\\(" $         ECap . Just . x_1
+      , mk "\\$\\("               $ const $ ECap Nothing
+      , mk "\\(\\?:"              $ const   PGrp
+      , mk "\\(\\?"               $ const   PCap
+      , mk "\\("                  $ const   Bra
+      , mk "\\\\(.)"              $         BS    . s2c . x_1
+      , mk "(.)"                  $         Other . s2c . x_1
       ]
 
     x_1     = captureText $ IsCaptureOrdinal $ CaptureOrdinal 1
@@ -139,35 +155,41 @@
 ------------------
 
 \begin{code}
+-- | format [Token] into an RE string
 formatTokens :: [Token] -> String
-formatTokens = formatTokens' defFormatTokenOptions
+formatTokens = formatTokens' defFormatTokenREOptions
 
-data FormatTokenOptions =
-  FormatTokenOptions
-    { _fto_regex_type :: Maybe RegexType
-    , _fto_min_caps   :: Bool
-    , _fto_incl_caps  :: Bool
+-- | options for the general Token formatter below
+data FormatTokenREOptions =
+  FormatTokenREOptions
+    { _fto_regex_type :: Maybe RegexType    -- ^ Posix, PCRE or indeterminate REs?
+    , _fto_min_caps   :: Bool               -- ^ remove captures where possible
+    , _fto_incl_caps  :: Bool               -- ^ include the captures in the output
     }
   deriving (Show)
 
-defFormatTokenOptions :: FormatTokenOptions
-defFormatTokenOptions =
-  FormatTokenOptions
+-- | the default configuration for the Token formatter
+defFormatTokenREOptions :: FormatTokenREOptions
+defFormatTokenREOptions =
+  FormatTokenREOptions
     { _fto_regex_type = Nothing
     , _fto_min_caps   = False
     , _fto_incl_caps  = False
     }
 
-idFormatTokenOptions :: FormatTokenOptions
-idFormatTokenOptions =
-  FormatTokenOptions
+-- | a configuration that will preserve the parsed regular expression
+-- in the output
+idFormatTokenREOptions :: FormatTokenREOptions
+idFormatTokenREOptions =
+  FormatTokenREOptions
     { _fto_regex_type = Nothing
     , _fto_min_caps   = False
     , _fto_incl_caps  = True
     }
 
-formatTokens' :: FormatTokenOptions -> [Token] -> String
-formatTokens' FormatTokenOptions{..} = foldr f ""
+-- | the general Token formatter, generating REs according to the options
+formatTokens' :: FormatTokenREOptions -> [Token] -> String
+formatTokens' FormatTokenREOptions{..} = foldr f ""
   where
     f tk tl = t_s ++ tl
       where
@@ -191,7 +213,7 @@
 \end{code}
 
 \begin{code}
--- this is a reference of formatTokens defFormatTokenOptions,
+-- this is a reference of formatTokens defFormatTokenREOptions,
 -- used for testing the latter
 formatTokens0 :: [Token] -> String
 formatTokens0 = foldr f ""
diff --git a/Text/RE/Internal/PreludeMacros.hs b/Text/RE/Internal/PreludeMacros.hs
--- a/Text/RE/Internal/PreludeMacros.hs
+++ b/Text/RE/Internal/PreludeMacros.hs
@@ -30,11 +30,12 @@
 import qualified Data.Text                    as T
 import           Data.Time
 import           Prelude.Compat
-import           Text.RE.Types.Options
+import           Text.RE.Types.REOptions
 import           Text.RE.TestBench.Parsers
 import           Text.RE.TestBench
 
 
+-- | generate the standard prelude Macros used to parse REs
 preludeMacros :: (Monad m,Functor m)
               => (String->m r)
               -> RegexType
@@ -42,21 +43,27 @@
               -> m (Macros r)
 preludeMacros prs rty wc = mkMacros prs rty wc $ preludeMacroEnv rty
 
+-- | format the standard prelude macros in a markdown table
 preludeMacroTable :: RegexType -> String
 preludeMacroTable rty = formatMacroTable rty $ preludeMacroEnv rty
 
+-- | generate a textual summary of the prelude macros
 preludeMacroSummary :: RegexType -> PreludeMacro -> String
 preludeMacroSummary rty =
   formatMacroSummary rty (preludeMacroEnv rty) . prelude_macro_id
 
+-- | generate a plain text table giving the RE for each macro with all
+-- macros expanded (to NF)
 preludeMacroSources :: RegexType -> String
 preludeMacroSources rty =
   formatMacroSources rty ExclCaptures $ preludeMacroEnv rty
 
+-- | generate plain text giving theexpanded RE for a single macro
 preludeMacroSource :: RegexType -> PreludeMacro -> String
 preludeMacroSource rty =
   formatMacroSource rty ExclCaptures (preludeMacroEnv rty) . prelude_macro_id
 
+-- | generate the `MacroEnv` for the standard prelude macros
 preludeMacroEnv :: RegexType -> MacroEnv
 preludeMacroEnv rty = fix $ prelude_macro_env rty
 
@@ -66,6 +73,7 @@
       | pm<-[minBound..maxBound]
       ]
 
+-- | generate the `MacroDescriptor` for a given `PreludeMacro`
 preludeMacroDescriptor :: RegexType
                        -> MacroEnv
                        -> PreludeMacro
diff --git a/Text/RE/Internal/QQ.hs b/Text/RE/Internal/QQ.hs
--- a/Text/RE/Internal/QQ.hs
+++ b/Text/RE/Internal/QQ.hs
@@ -7,15 +7,18 @@
 import           Language.Haskell.TH.Quote
 
 
+-- | used to throw an exception reporting an abuse of a quasi quoter
 data QQFailure =
   QQFailure
-    { _qqf_context   :: String
-    , _qqf_component :: String
+    { _qqf_context   :: String  -- ^ in what context was the quasi quoter used
+    , _qqf_component :: String  -- ^ how was the quasi quoter being abused
     }
   deriving (Show,Typeable)
 
 instance Exception QQFailure where
 
+-- | a quasi quoter that can be used in no context (to be extended with
+-- the appropriate quasi quoter parser)
 qq0 :: String -> QuasiQuoter
 qq0 ctx =
   QuasiQuoter
diff --git a/Text/RE/SearchReplace.hs b/Text/RE/SearchReplace.hs
new file mode 100644
--- /dev/null
+++ b/Text/RE/SearchReplace.hs
@@ -0,0 +1,99 @@
+{-# LANGUAGE NoImplicitPrelude          #-}
+{-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE TypeSynonymInstances       #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE CPP                        #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+{-# LANGUAGE TemplateHaskellQuotes      #-}
+#else
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE TemplateHaskell            #-}
+#endif
+{-# OPTIONS_GHC -fno-warn-orphans       #-}
+
+module Text.RE.SearchReplace
+  (
+  -- * Serach and Replace
+    SearchReplace(..)
+  , searchReplaceFirst
+  , searchReplaceAll
+  , unsafeCompileSearchReplace_
+  , compileSearchReplace_
+  , compileSearchAndReplace_
+  ) where
+
+import qualified Data.HashMap.Strict            as HMS
+import           Prelude.Compat
+import           Text.RE.Internal.NamedCaptures
+import           Text.RE.Types.Capture
+import           Text.RE.Types.CaptureID
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.Matches
+import           Text.RE.Types.Replace
+import           Text.RE.Types.SearchReplace
+import qualified Text.Regex.TDFA                as TDFA
+
+
+-- | search and replace
+searchReplaceAll, searchReplaceFirst :: IsRegex re s => SearchReplace re s -> s -> s
+searchReplaceAll   SearchReplace{..} = replaceAll getTemplate . matchMany getSearch
+searchReplaceFirst SearchReplace{..} = replace    getTemplate . matchOnce getSearch
+
+-- | warapper on 'compileSearchReplace_' that will generate an error
+-- if any compilation errors are found
+unsafeCompileSearchReplace_ :: (String->s)
+                            -> (String->Either String re)
+                            -> String
+                            -> SearchReplace re s
+unsafeCompileSearchReplace_ pk cf = either err id . compileSearchReplace_ pk cf
+  where
+    err msg = error $ "unsafeCompileSearchReplace_: " ++ msg
+
+-- | compile a SearchReplace template generating errors if the RE or
+-- the template are not well formed -- all capture references being checked
+compileSearchReplace_ :: (Monad m,Functor m)
+                      => (String->s)
+                      -> (String->Either String re)
+                      -> String
+                      -> m (SearchReplace re s)
+compileSearchReplace_ pack compile_re sr_tpl = either fail return $ do
+    case mainCaptures $ sr_tpl $=~ "///" of
+      [cap] ->
+        compileSearchAndReplace_ pack compile_re
+                      (capturePrefix cap) (captureSuffix cap)
+      _ -> Left $ "bad search-replace template syntax: " ++ sr_tpl
+
+-- | compile 'SearcgReplace' from two strings containing the RE
+-- and the replacement template
+compileSearchAndReplace_ :: (Monad m,Functor m)
+                         => (String->s)
+                         -> (String->Either String re)
+                         -> String
+                         -> String
+                         -> m (SearchReplace re s)
+compileSearchAndReplace_ pack compile_re re_s tpl = either fail return $ do
+    re           <- compile_re re_s
+    ((n,cnms),_) <- extractNamedCaptures re_s
+    mapM_ (check n cnms) $ templateCaptures id tpl
+    return $ SearchReplace re $ pack tpl
+  where
+    check :: Int -> CaptureNames -> CaptureID -> Either String ()
+    check n cnms cid = case cid of
+      IsCaptureOrdinal co -> check_co n    co
+      IsCaptureName    cn -> check_cn cnms cn
+
+    check_co n (CaptureOrdinal i) = case i <= n of
+      True  -> return ()
+      False -> Left $ "capture ordinal out of range: " ++
+                                      show i ++ " >= " ++ show n
+
+    check_cn cnms cnm = case cnm `HMS.member` cnms of
+      True  -> return ()
+      False -> Left $ "capture name not defined: " ++
+                                      show (getCaptureName cnm)
+
+($=~) :: String -> String -> Matches String
+($=~) = (TDFA.=~)
diff --git a/Text/RE/Summa.hs b/Text/RE/Summa.hs
new file mode 100644
--- /dev/null
+++ b/Text/RE/Summa.hs
@@ -0,0 +1,20 @@
+module Text.RE.Summa
+  ( -- $collection
+    module Text.RE.SearchReplace
+  , module Text.RE.TestBench
+  , module Text.RE.Tools
+  , module Text.RE.Types
+  ) where
+
+import Text.RE.SearchReplace
+import Text.RE.TestBench
+import Text.RE.Tools
+import Text.RE.Types
+
+-- $collection
+--
+-- This module collects together all of the generic regex APIs not
+-- exported by the API modules specific to each back end. The regex
+-- API is modular with only the most common types and functions being
+-- exported by these modules. The remining modules may be imported
+-- individually or en masse by importing this module.
diff --git a/Text/RE/TDFA.hs b/Text/RE/TDFA.hs
--- a/Text/RE/TDFA.hs
+++ b/Text/RE/TDFA.hs
@@ -1,5 +1,6 @@
-{-# LANGUAGE FlexibleContexts           #-}
-{-# LANGUAGE CPP                        #-}
+{-# LANGUAGE FlexibleContexts               #-}
+{-# LANGUAGE CPP                            #-}
+{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}
 #if __GLASGOW_HASKELL__ >= 800
 {-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
 #endif
@@ -9,17 +10,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
-  -- * The Overloaded Match Operators
+  --
+  -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
+  , RE
+  , SimpleREOptions(..)
+  , reSource
+  , compileRegex
+  , compileRegexWith
+  , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   -- * The Operator Instances
   -- $instances
@@ -31,12 +51,12 @@
   , module Text.RE.TDFA.Text.Lazy
   ) where
 
-
 import qualified Text.Regex.Base                          as B
 import           Text.RE
 import           Text.RE.Internal.AddCaptureNames
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA                          as TDFA
+import           Text.RE.SearchReplace
 import           Text.RE.TDFA.ByteString()
 import           Text.RE.TDFA.ByteString.Lazy()
 import           Text.RE.TDFA.Sequence()
@@ -44,6 +64,7 @@
 import           Text.RE.TDFA.Text()
 import           Text.RE.TDFA.Text.Lazy()
 import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
 
 
 -- | find all matches in text
@@ -60,6 +81,14 @@
       -> Match s
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ matchOnce rex bs
 
+-- | search and replace once
+(?=~/) :: IsRegex RE s => s -> SearchReplace RE s -> s
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: IsRegex RE s => s -> SearchReplace RE s -> s
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( B.RegexContext TDFA.Regex s a
         , B.RegexMaker   TDFA.Regex TDFA.CompOption TDFA.ExecOption s
@@ -80,19 +109,7 @@
 (=~~) bs rex = B.matchM (reRegex rex) bs
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
 
 -- $instances
 --
diff --git a/Text/RE/TDFA/ByteString.hs b/Text/RE/TDFA/ByteString.hs
--- a/Text/RE/TDFA/ByteString.hs
+++ b/Text/RE/TDFA/ByteString.hs
@@ -13,22 +13,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
+  --
   -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type and functions
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
   , RE
+  , SimpleREOptions(..)
   , reSource
   , compileRegex
   , compileRegexWith
   , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   ) where
 
@@ -37,8 +51,11 @@
 import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE
-import           Text.RE.Types.IsRegex
 import           Text.RE.Internal.AddCaptureNames
+import           Text.RE.SearchReplace
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA               as TDFA
 
@@ -55,6 +72,14 @@
       -> Match B.ByteString
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs
 
+-- | search and replace once
+(?=~/) :: B.ByteString -> SearchReplace RE B.ByteString -> B.ByteString
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: B.ByteString -> SearchReplace RE B.ByteString -> B.ByteString
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( Typeable a
         , RegexContext TDFA.Regex B.ByteString a
@@ -78,23 +103,11 @@
 (=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs
 
 instance IsRegex RE B.ByteString where
-  matchOnce     = flip (?=~)
-  matchMany     = flip (*=~)
-  makeRegexWith = \o -> compileRegexWith o . unpackE
-  makeRegex     = compileRegex . unpackE
-  regexSource   = packE . reSource
+  matchOnce             = flip (?=~)
+  matchMany             = flip (*=~)
+  makeRegexWith         = \o -> compileRegexWith o . unpackR
+  makeSearchReplaceWith = \o r t -> compileSearchReplaceWith o (unpackR r) (unpackR t)
+  regexSource           = packR . reSource
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
diff --git a/Text/RE/TDFA/ByteString/Lazy.hs b/Text/RE/TDFA/ByteString/Lazy.hs
--- a/Text/RE/TDFA/ByteString/Lazy.hs
+++ b/Text/RE/TDFA/ByteString/Lazy.hs
@@ -13,22 +13,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
+  --
   -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type and functions
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
   , RE
+  , SimpleREOptions(..)
   , reSource
   , compileRegex
   , compileRegexWith
   , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   ) where
 
@@ -37,8 +51,11 @@
 import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE
-import           Text.RE.Types.IsRegex
 import           Text.RE.Internal.AddCaptureNames
+import           Text.RE.SearchReplace
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA               as TDFA
 
@@ -55,6 +72,14 @@
       -> Match LBS.ByteString
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs
 
+-- | search and replace once
+(?=~/) :: LBS.ByteString -> SearchReplace RE LBS.ByteString -> LBS.ByteString
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: LBS.ByteString -> SearchReplace RE LBS.ByteString -> LBS.ByteString
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( Typeable a
         , RegexContext TDFA.Regex LBS.ByteString a
@@ -78,23 +103,11 @@
 (=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs
 
 instance IsRegex RE LBS.ByteString where
-  matchOnce     = flip (?=~)
-  matchMany     = flip (*=~)
-  makeRegexWith = \o -> compileRegexWith o . unpackE
-  makeRegex     = compileRegex . unpackE
-  regexSource   = packE . reSource
+  matchOnce             = flip (?=~)
+  matchMany             = flip (*=~)
+  makeRegexWith         = \o -> compileRegexWith o . unpackR
+  makeSearchReplaceWith = \o r t -> compileSearchReplaceWith o (unpackR r) (unpackR t)
+  regexSource           = packR . reSource
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
diff --git a/Text/RE/TDFA/RE.hs b/Text/RE/TDFA/RE.hs
--- a/Text/RE/TDFA/RE.hs
+++ b/Text/RE/TDFA/RE.hs
@@ -1,7 +1,5 @@
 {-# LANGUAGE NoImplicitPrelude          #-}
 {-# LANGUAGE RecordWildCards            #-}
-{-# LANGUAGE QuasiQuotes                #-}
-{-# LANGUAGE TemplateHaskell            #-}
 {-# LANGUAGE MultiParamTypeClasses      #-}
 {-# LANGUAGE TypeSynonymInstances       #-}
 {-# LANGUAGE FlexibleInstances          #-}
@@ -9,30 +7,39 @@
 {-# LANGUAGE CPP                        #-}
 #if __GLASGOW_HASKELL__ >= 800
 {-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+{-# LANGUAGE TemplateHaskellQuotes      #-}
+#else
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE TemplateHaskell            #-}
 #endif
 {-# OPTIONS_GHC -fno-warn-orphans       #-}
 
 module Text.RE.TDFA.RE
-  ( re
-  , reMS
-  , reMI
-  , reBS
-  , reBI
-  , reMultilineSensitive
-  , reMultilineInsensitive
-  , reBlockSensitive
-  , reBlockInsensitive
-  , re_
-  , cp
+  ( -- * RE Type
+    RE
   , regexType
-  , RE
   , reOptions
   , reSource
   , reCaptureNames
   , reRegex
-  , Options
-  , noPreludeOptions
-  , defaultOptions
+  -- * REOptions Type
+  , REOptions
+  , defaultREOptions
+  , noPreludeREOptions
+  -- * Compiling Regular Expressions
+  , compileRegex
+  , compileRegexWith
+  , compileRegexWithOptions
+  -- * Compiling Search-Replace Templates
+  , compileSearchReplace
+  , compileSearchReplaceWith
+  , compileSearchReplaceWithREOptions
+  -- * Escaping String
+  , escape
+  , escapeWith
+  , escapeWithOptions
+  , escapeREString
+  -- * Macros Standard Environment
   , prelude
   , preludeEnv
   , preludeTestsFailing
@@ -40,110 +47,121 @@
   , preludeSummary
   , preludeSources
   , preludeSource
-  , unpackSimpleRegexOptions
-  , compileRegex
-  , compileRegexWith
-  , compileRegexWithOptions
-  , escape
-  , escapeREString
+  , unpackSimpleREOptions
+  -- * The Quasi Quoters
+  , re
+  , reMS
+  , reMI
+  , reBS
+  , reBI
+  , reMultilineSensitive
+  , reMultilineInsensitive
+  , reBlockSensitive
+  , reBlockInsensitive
+  , re_
+  , ed
+  , edMS
+  , edMI
+  , edBS
+  , edBI
+  , edMultilineSensitive
+  , edMultilineInsensitive
+  , edBlockSensitive
+  , edBlockInsensitive
+  , ed_
+  , cp
   ) where
 
 import           Data.Functor.Identity
 import           Language.Haskell.TH
 import           Language.Haskell.TH.Quote
 import           Prelude.Compat
-import           Text.RE
 import           Text.RE.Internal.EscapeREString
 import           Text.RE.Internal.NamedCaptures
 import           Text.RE.Internal.PreludeMacros
 import           Text.RE.Internal.QQ
+import           Text.RE.SearchReplace
 import           Text.RE.TestBench
 import           Text.RE.Types.CaptureID
-import           Text.RE.Types.Options
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.Regex.TDFA
 
 
-re
-  , reMS
-  , reMI
-  , reBS
-  , reBI
-  , reMultilineSensitive
-  , reMultilineInsensitive
-  , reBlockSensitive
-  , reBlockInsensitive
-  , re_ :: QuasiQuoter
-
-re                       = re' $ Just minBound
-reMS                     = reMultilineSensitive
-reMI                     = reMultilineInsensitive
-reBS                     = reBlockSensitive
-reBI                     = reBlockInsensitive
-reMultilineSensitive     = re' $ Just  MultilineSensitive
-reMultilineInsensitive   = re' $ Just  MultilineInsensitive
-reBlockSensitive         = re' $ Just  BlockSensitive
-reBlockInsensitive       = re' $ Just  BlockInsensitive
-re_                      = re'   Nothing
-
-regexType :: RegexType
-regexType =
-  mkTDFA $ \txt env md -> txt =~ mdRegexSource regexType ExclCaptures env md
-
+-- | the RE type for this back end representing a well-formed, compiled
+-- RE
 data RE =
   RE
-    { _re_options :: !Options
+    { _re_options :: !REOptions
     , _re_source  :: !String
     , _re_cnames  :: !CaptureNames
     , _re_regex   :: !Regex
     }
 
-reOptions :: RE -> Options
+-- | some functions in the "Text.RE.TestBench" need the back end to
+-- be passed dynamically as a 'RegexType' parameters: use 'regexType'
+-- fpr this backend
+regexType :: RegexType
+regexType =
+  mkTDFA $ \txt env md -> txt =~ mdRegexSource regexType ExclCaptures env md
+
+-- | extract the 'REOptions' from the @RE@
+reOptions :: RE -> REOptions
 reOptions = _re_options
 
+-- | extract the RE source string from the @RE@
 reSource :: RE -> String
 reSource = _re_source
 
+-- | extract the 'CaptureNames' from the @RE@
 reCaptureNames :: RE -> CaptureNames
 reCaptureNames = _re_cnames
 
+-- | extract the back end compiled 'Regex' type from the @RE@
 reRegex :: RE -> Regex
 reRegex = _re_regex
 
-type Options = Options_ RE CompOption ExecOption
 
-instance IsOption SimpleRegexOptions RE CompOption ExecOption where
-  makeOptions    = unpackSimpleRegexOptions
+------------------------------------------------------------------------
+-- REOptions
+------------------------------------------------------------------------
 
+-- | and the REOptions for this back end (see "Text.RE.Types.REOptions"
+-- for details)
+type REOptions = REOptions_ RE CompOption ExecOption
+
+instance IsOption SimpleREOptions RE CompOption ExecOption where
+  makeREOptions    = unpackSimpleREOptions
+
 instance IsOption (Macros RE) RE CompOption ExecOption where
-  makeOptions ms = Options ms def_comp_option def_exec_option
+  makeREOptions ms = REOptions ms def_comp_option def_exec_option
 
 instance IsOption CompOption  RE CompOption ExecOption where
-  makeOptions co = Options prelude co def_exec_option
+  makeREOptions co = REOptions prelude co def_exec_option
 
 instance IsOption ExecOption  RE CompOption ExecOption where
-  makeOptions eo = Options prelude def_comp_option eo
+  makeREOptions eo = REOptions prelude def_comp_option eo
 
-instance IsOption Options     RE CompOption ExecOption where
-  makeOptions    = id
+instance IsOption REOptions     RE CompOption ExecOption where
+  makeREOptions    = id
 
 instance IsOption ()          RE CompOption ExecOption where
-  makeOptions _  = unpackSimpleRegexOptions minBound
-
-def_comp_option :: CompOption
-def_comp_option = optionsComp defaultOptions
-
-def_exec_option :: ExecOption
-def_exec_option = optionsExec defaultOptions
+  makeREOptions _  = unpackSimpleREOptions minBound
 
-noPreludeOptions :: Options
-noPreludeOptions = defaultOptions { optionsMacs = emptyMacros }
+-- | the default 'REOptions'
+defaultREOptions :: REOptions
+defaultREOptions = makeREOptions (minBound::SimpleREOptions)
 
-defaultOptions :: Options
-defaultOptions = makeOptions (minBound::SimpleRegexOptions)
+-- | the default 'REOptions' but with no RE macros defined
+noPreludeREOptions :: REOptions
+noPreludeREOptions = defaultREOptions { optionsMacs = emptyMacros }
 
-unpackSimpleRegexOptions :: SimpleRegexOptions -> Options
-unpackSimpleRegexOptions sro =
-  Options
+-- | convert a universal 'SimpleReOptions' into the 'REOptions' used
+-- by this back end
+unpackSimpleREOptions :: SimpleREOptions -> REOptions
+unpackSimpleREOptions sro =
+  REOptions
     { optionsMacs = prelude
     , optionsComp = comp
     , optionsExec = defaultExecOpt
@@ -160,12 +178,23 @@
         BlockSensitive        -> (,) False True
         BlockInsensitive      -> (,) False False
 
+
+------------------------------------------------------------------------
+-- Compiling Regular Expressions
+------------------------------------------------------------------------
+
+-- | compile a 'String' into a 'RE' with the default options,
+-- generating an error if the RE is not well formed
 compileRegex :: (Functor m,Monad m) => String -> m RE
 compileRegex = compileRegexWithOptions ()
 
-compileRegexWith :: (Functor m,Monad m) => SimpleRegexOptions -> String -> m RE
+-- | compile a 'String' into a 'RE' using the given @SimpleREOptions@,
+-- generating an error if the RE is not well formed
+compileRegexWith :: (Functor m,Monad m) => SimpleREOptions -> String -> m RE
 compileRegexWith = compileRegexWithOptions
 
+-- | compile a 'String' into a 'RE' using the given @SimpleREOptions@,
+-- generating an error if the RE is not well formed
 compileRegexWithOptions :: ( IsOption o RE CompOption ExecOption
                            , Functor m
                            , Monad   m
@@ -173,86 +202,273 @@
                         => o
                         -> String
                         -> m RE
-compileRegexWithOptions = compileRegex_ . makeOptions
+compileRegexWithOptions = compileRegex_ . makeREOptions
 
-compileRegex_ :: (Functor m,Monad m)
-              => Options
-              -> String
-              -> m RE
-compileRegex_ os re_s = uncurry mk <$> compileRegex' os re_s
+
+------------------------------------------------------------------------
+-- Compiling Search Replace Templates
+------------------------------------------------------------------------
+
+-- | compile a SearchReplace template generating errors if the RE or
+-- the template are not well formed -- all capture references being checked
+compileSearchReplace :: (Monad m,Functor m,IsRegex RE s)
+                     => String
+                     -> String
+                     -> m (SearchReplace RE s)
+compileSearchReplace = compileSearchReplaceWith minBound
+
+-- | compile a SearchReplace template, with simple options, generating
+-- errors if the RE or the template are not well formed -- all capture
+-- references being checked
+compileSearchReplaceWith :: (Monad m,Functor m,IsRegex RE s)
+                         => SimpleREOptions
+                         -> String
+                         -> String
+                         -> m (SearchReplace RE s)
+compileSearchReplaceWith sro = compileSearchAndReplace_ packR $ compileRegexWith sro
+
+-- | compile a SearchReplace template, with general options, generating
+-- errors if the RE or the template are not well formed -- all capture
+-- references being checked
+compileSearchReplaceWithREOptions :: (Monad m,Functor m,IsRegex RE s)
+                                  => REOptions
+                                  -> String
+                                  -> String
+                                  -> m (SearchReplace RE s)
+compileSearchReplaceWithREOptions os = compileSearchAndReplace_ packR $ compileRegexWithOptions os
+
+
+------------------------------------------------------------------------
+-- Escaping Strings
+------------------------------------------------------------------------
+
+-- | convert a string into a RE that matches that string, and apply it
+-- to an argument continuation function to make up the RE string to be
+-- compiled
+escape :: (Functor m,Monad m)
+       => (String->String)
+       -> String
+       -> m RE
+escape = escapeWith minBound
+
+-- | convert a string into a RE that matches that string, and apply it
+-- to an argument continuation function to make up the RE string to be
+-- compiled with the default options
+escapeWith :: (Functor m,Monad m)
+           => SimpleREOptions
+           -> (String->String)
+           -> String
+           -> m RE
+escapeWith = escapeWithOptions
+
+-- | convert a string into a RE that matches that string, and apply it
+-- to an argument continuation function to make up the RE string to be
+-- compiled the given options
+escapeWithOptions :: ( IsOption o RE CompOption ExecOption
+                     , Functor m
+                     , Monad m
+                     )
+                  => o
+                  -> (String->String)
+                  -> String
+                  -> m RE
+escapeWithOptions o f = compileRegexWithOptions o . f . escapeREString
+
+
+------------------------------------------------------------------------
+-- Macro Standard Environment
+------------------------------------------------------------------------
+
+-- | the standard table of 'Macros' used to compile REs (which can be
+-- extended or replace: see "Text.RE.TestBench")
+prelude :: Macros RE
+prelude = runIdentity $ preludeMacros mk regexType ExclCaptures
   where
-    mk cnms rx =
-      RE
-        { _re_options = os
-        , _re_source  = re_s
-        , _re_cnames  = cnms
-        , _re_regex   = rx
-        }
+    mk = Identity . unsafeCompileRegex_ noPreludeREOptions
 
-re' :: Maybe SimpleRegexOptions -> QuasiQuoter
+-- | the standard 'MacroEnv' for this back end (see "Text.RE.TestBench")
+preludeEnv :: MacroEnv
+preludeEnv = preludeMacroEnv regexType
+
+-- | the macros in the standard environment that are failing their tests
+-- (checked by the test suite to be empty)
+preludeTestsFailing :: [MacroID]
+preludeTestsFailing = badMacros $ preludeMacroEnv regexType
+
+-- | a table the standard macros in markdown format
+preludeTable :: String
+preludeTable = preludeMacroTable regexType
+
+-- | a summary of the macros in the standard environment for this back
+-- end in plain text
+preludeSummary :: PreludeMacro -> String
+preludeSummary = preludeMacroSummary regexType
+
+-- | a listing of the RE text for each macro in the standard environment
+-- with all macros expanded to normal form
+preludeSources :: String
+preludeSources = preludeMacroSources regexType
+
+-- | the prolude source of a given macro in the standard environment
+preludeSource :: PreludeMacro -> String
+preludeSource = preludeMacroSource regexType
+
+
+------------------------------------------------------------------------
+-- Quasi Quoters
+------------------------------------------------------------------------
+
+-- | the @[re| ... |]@ and @[ed| ... /// ... |]@ quasi quoters
+re
+  , reMS
+  , reMI
+  , reBS
+  , reBI
+  , reMultilineSensitive
+  , reMultilineInsensitive
+  , reBlockSensitive
+  , reBlockInsensitive
+  , re_
+  , ed
+  , edMS
+  , edMI
+  , edBS
+  , edBI
+  , edMultilineSensitive
+  , edMultilineInsensitive
+  , edBlockSensitive
+  , edBlockInsensitive
+  , ed_ :: QuasiQuoter
+
+re                       = re' $ Just minBound
+reMS                     = reMultilineSensitive
+reMI                     = reMultilineInsensitive
+reBS                     = reBlockSensitive
+reBI                     = reBlockInsensitive
+reMultilineSensitive     = re' $ Just  MultilineSensitive
+reMultilineInsensitive   = re' $ Just  MultilineInsensitive
+reBlockSensitive         = re' $ Just  BlockSensitive
+reBlockInsensitive       = re' $ Just  BlockInsensitive
+re_                      = re'   Nothing
+
+ed                       = ed' $ Just minBound
+edMS                     = edMultilineSensitive
+edMI                     = edMultilineInsensitive
+edBS                     = edBlockSensitive
+edBI                     = edBlockInsensitive
+edMultilineSensitive     = ed' $ Just  MultilineSensitive
+edMultilineInsensitive   = ed' $ Just  MultilineInsensitive
+edBlockSensitive         = ed' $ Just  BlockSensitive
+edBlockInsensitive       = ed' $ Just  BlockInsensitive
+ed_                      = ed'   Nothing
+
+
+------------------------------------------------------------------------
+-- re Helpers
+------------------------------------------------------------------------
+
+re' :: Maybe SimpleREOptions -> QuasiQuoter
 re' mb = case mb of
   Nothing  ->
-    (qq0 "re_")
+    (qq0 "re'")
       { quoteExp = parse minBound (\rs->[|flip unsafeCompileRegex rs|])
       }
   Just sro ->
-    (qq0 "re")
+    (qq0 "re'")
       { quoteExp = parse sro (\rs->[|unsafeCompileRegexSimple sro rs|])
       }
   where
-    parse :: SimpleRegexOptions -> (String->Q Exp) -> String -> Q Exp
+    parse :: SimpleREOptions -> (String->Q Exp) -> String -> Q Exp
     parse sro mk rs = either error (\_->mk rs) $ compileRegex_ os rs
       where
-        os = unpackSimpleRegexOptions sro
+        os = unpackSimpleREOptions sro
 
-unsafeCompileRegexSimple :: SimpleRegexOptions -> String -> RE
+unsafeCompileRegexSimple :: SimpleREOptions -> String -> RE
 unsafeCompileRegexSimple sro re_s = unsafeCompileRegex_ os re_s
   where
-    os = unpackSimpleRegexOptions sro
+    os = unpackSimpleREOptions sro
 
 unsafeCompileRegex :: IsOption o RE CompOption ExecOption
                    => o
                    -> String
                    -> RE
-unsafeCompileRegex = unsafeCompileRegex_ . makeOptions
+unsafeCompileRegex = unsafeCompileRegex_ . makeREOptions
 
-unsafeCompileRegex_ :: Options -> String -> RE
+unsafeCompileRegex_ :: REOptions -> String -> RE
 unsafeCompileRegex_ os = either oops id . compileRegex_ os
   where
     oops = error . ("unsafeCompileRegex: " ++)
 
 compileRegex' :: (Functor m,Monad m)
-              => Options
+              => REOptions
               -> String
               -> m (CaptureNames,Regex)
-compileRegex' Options{..} s0 = do
-    (cnms,s2) <- either fail return $ extractNamedCaptures s1
+compileRegex' REOptions{..} s0 = do
+    ((_,cnms),s2) <- either fail return $ extractNamedCaptures s1
     (,) cnms <$> makeRegexOptsM optionsComp optionsExec s2
   where
     s1 = expandMacros reSource optionsMacs s0
 
-prelude :: Macros RE
-prelude = runIdentity $ preludeMacros mk regexType ExclCaptures
+compileRegex_ :: (Functor m,Monad m)
+              => REOptions
+              -> String
+              -> m RE
+compileRegex_ os re_s = uncurry mk <$> compileRegex' os re_s
   where
-    mk = Identity . unsafeCompileRegex_ noPreludeOptions
+    mk cnms rx =
+      RE
+        { _re_options = os
+        , _re_source  = re_s
+        , _re_cnames  = cnms
+        , _re_regex   = rx
+        }
 
-preludeEnv :: MacroEnv
-preludeEnv = preludeMacroEnv regexType
 
-preludeTestsFailing :: [MacroID]
-preludeTestsFailing = badMacros $ preludeMacroEnv regexType
+------------------------------------------------------------------------
+-- ed Helpers
+------------------------------------------------------------------------
 
-preludeTable :: String
-preludeTable = preludeMacroTable regexType
+ed' :: Maybe SimpleREOptions -> QuasiQuoter
+ed' mb = case mb of
+  Nothing  ->
+    (qq0 "ed'")
+      { quoteExp = parse minBound (\rs->[|flip unsafeCompileSearchReplace rs|])
+      }
+  Just sro ->
+    (qq0 "ed'")
+      { quoteExp = parse sro (\rs->[|unsafeCompileSearchReplaceSimple sro rs|])
+      }
+  where
+    parse :: SimpleREOptions -> (String->Q Exp) -> String -> Q Exp
+    parse sro mk ts = either error (\_->mk ts) ei
+      where
+        ei :: Either String (SearchReplace RE String)
+        ei = compileSearchReplace_ id (compileRegexWith sro) ts
 
-preludeSummary :: PreludeMacro -> String
-preludeSummary = preludeMacroSummary regexType
+unsafeCompileSearchReplaceSimple :: IsRegex RE s
+                                 => SimpleREOptions
+                                 -> String
+                                 -> SearchReplace RE s
+unsafeCompileSearchReplaceSimple sro re_s = unsafeCompileSearchReplace os re_s
+  where
+    os = unpackSimpleREOptions sro
 
-preludeSources :: String
-preludeSources = preludeMacroSources regexType
+unsafeCompileSearchReplace :: ( IsOption o RE CompOption ExecOption
+                              , IsRegex RE s
+                              )
+                           => o
+                           -> String
+                           -> SearchReplace RE s
+unsafeCompileSearchReplace os =
+    unsafeCompileSearchReplace_ packR $ compileRegexWithOptions os
 
-preludeSource :: PreludeMacro -> String
-preludeSource = preludeMacroSource regexType
 
-escape :: (String->String) -> String -> RE
-escape f = unsafeCompileRegex () . f . escapeREString
+------------------------------------------------------------------------
+-- Options Helpers
+------------------------------------------------------------------------
+
+def_comp_option :: CompOption
+def_comp_option = optionsComp defaultREOptions
+
+def_exec_option :: ExecOption
+def_exec_option = optionsExec defaultREOptions
diff --git a/Text/RE/TDFA/Sequence.hs b/Text/RE/TDFA/Sequence.hs
--- a/Text/RE/TDFA/Sequence.hs
+++ b/Text/RE/TDFA/Sequence.hs
@@ -13,22 +13,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
+  --
   -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type and functions
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
   , RE
+  , SimpleREOptions(..)
   , reSource
   , compileRegex
   , compileRegexWith
   , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   ) where
 
@@ -37,8 +51,11 @@
 import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE
-import           Text.RE.Types.IsRegex
 import           Text.RE.Internal.AddCaptureNames
+import           Text.RE.SearchReplace
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA               as TDFA
 
@@ -55,6 +72,14 @@
       -> Match (S.Seq Char)
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs
 
+-- | search and replace once
+(?=~/) :: (S.Seq Char) -> SearchReplace RE (S.Seq Char) -> (S.Seq Char)
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: (S.Seq Char) -> SearchReplace RE (S.Seq Char) -> (S.Seq Char)
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( Typeable a
         , RegexContext TDFA.Regex (S.Seq Char) a
@@ -78,23 +103,11 @@
 (=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs
 
 instance IsRegex RE (S.Seq Char) where
-  matchOnce     = flip (?=~)
-  matchMany     = flip (*=~)
-  makeRegexWith = \o -> compileRegexWith o . unpackE
-  makeRegex     = compileRegex . unpackE
-  regexSource   = packE . reSource
+  matchOnce             = flip (?=~)
+  matchMany             = flip (*=~)
+  makeRegexWith         = \o -> compileRegexWith o . unpackR
+  makeSearchReplaceWith = \o r t -> compileSearchReplaceWith o (unpackR r) (unpackR t)
+  regexSource           = packR . reSource
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
diff --git a/Text/RE/TDFA/String.hs b/Text/RE/TDFA/String.hs
--- a/Text/RE/TDFA/String.hs
+++ b/Text/RE/TDFA/String.hs
@@ -13,22 +13,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
+  --
   -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type and functions
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
   , RE
+  , SimpleREOptions(..)
   , reSource
   , compileRegex
   , compileRegexWith
   , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   ) where
 
@@ -37,8 +51,11 @@
 import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE
-import           Text.RE.Types.IsRegex
 import           Text.RE.Internal.AddCaptureNames
+import           Text.RE.SearchReplace
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA               as TDFA
 
@@ -55,6 +72,14 @@
       -> Match String
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs
 
+-- | search and replace once
+(?=~/) :: String -> SearchReplace RE String -> String
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: String -> SearchReplace RE String -> String
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( Typeable a
         , RegexContext TDFA.Regex String a
@@ -78,23 +103,11 @@
 (=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs
 
 instance IsRegex RE String where
-  matchOnce     = flip (?=~)
-  matchMany     = flip (*=~)
-  makeRegexWith = \o -> compileRegexWith o . unpackE
-  makeRegex     = compileRegex . unpackE
-  regexSource   = packE . reSource
+  matchOnce             = flip (?=~)
+  matchMany             = flip (*=~)
+  makeRegexWith         = \o -> compileRegexWith o . unpackR
+  makeSearchReplaceWith = \o r t -> compileSearchReplaceWith o (unpackR r) (unpackR t)
+  regexSource           = packR . reSource
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
diff --git a/Text/RE/TDFA/Text.hs b/Text/RE/TDFA/Text.hs
--- a/Text/RE/TDFA/Text.hs
+++ b/Text/RE/TDFA/Text.hs
@@ -13,22 +13,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
+  --
   -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type and functions
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
   , RE
+  , SimpleREOptions(..)
   , reSource
   , compileRegex
   , compileRegexWith
   , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   ) where
 
@@ -37,8 +51,11 @@
 import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE
-import           Text.RE.Types.IsRegex
 import           Text.RE.Internal.AddCaptureNames
+import           Text.RE.SearchReplace
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA               as TDFA
 
@@ -55,6 +72,14 @@
       -> Match T.Text
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs
 
+-- | search and replace once
+(?=~/) :: T.Text -> SearchReplace RE T.Text -> T.Text
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: T.Text -> SearchReplace RE T.Text -> T.Text
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( Typeable a
         , RegexContext TDFA.Regex T.Text a
@@ -78,23 +103,11 @@
 (=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs
 
 instance IsRegex RE T.Text where
-  matchOnce     = flip (?=~)
-  matchMany     = flip (*=~)
-  makeRegexWith = \o -> compileRegexWith o . unpackE
-  makeRegex     = compileRegex . unpackE
-  regexSource   = packE . reSource
+  matchOnce             = flip (?=~)
+  matchMany             = flip (*=~)
+  makeRegexWith         = \o -> compileRegexWith o . unpackR
+  makeSearchReplaceWith = \o r t -> compileSearchReplaceWith o (unpackR r) (unpackR t)
+  regexSource           = packR . reSource
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
diff --git a/Text/RE/TDFA/Text/Lazy.hs b/Text/RE/TDFA/Text/Lazy.hs
--- a/Text/RE/TDFA/Text/Lazy.hs
+++ b/Text/RE/TDFA/Text/Lazy.hs
@@ -13,22 +13,36 @@
   (
   -- * Tutorial
   -- $tutorial
-
+  --
   -- * The Match Operators
     (*=~)
   , (?=~)
+  -- * The SearchReplace Operators
+  , (*=~/)
+  , (?=~/)
+  -- * The Classic rexex-base Match Operators
   , (=~)
   , (=~~)
-  -- * The Toolkit
-  -- $toolkit
-  , module Text.RE
-  -- * The 'RE' Type and functions
-  -- $re
+  -- * Matches
+  , Matches
+  , matchesSource
+  , allMatches
+  , anyMatches
+  , countMatches
+  , matches
+  -- * Match
+  , Match
+  , matchSource
+  , matched
+  , matchedText
+  -- * The 'RE' Type and Functions
   , RE
+  , SimpleREOptions(..)
   , reSource
   , compileRegex
   , compileRegexWith
   , escape
+  , escapeWith
   , module Text.RE.TDFA.RE
   ) where
 
@@ -37,8 +51,11 @@
 import           Data.Typeable
 import           Text.Regex.Base
 import           Text.RE
-import           Text.RE.Types.IsRegex
 import           Text.RE.Internal.AddCaptureNames
+import           Text.RE.SearchReplace
+import           Text.RE.Types.IsRegex
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Replace
 import           Text.RE.TDFA.RE
 import qualified Text.Regex.TDFA               as TDFA
 
@@ -55,6 +72,14 @@
       -> Match TL.Text
 (?=~) bs rex = addCaptureNamesToMatch (reCaptureNames rex) $ match (reRegex rex) bs
 
+-- | search and replace once
+(?=~/) :: TL.Text -> SearchReplace RE TL.Text -> TL.Text
+(?=~/) = flip searchReplaceFirst
+
+-- | search and replace, all occurrences
+(*=~/) :: TL.Text -> SearchReplace RE TL.Text -> TL.Text
+(*=~/) = flip searchReplaceAll
+
 -- | the regex-base polymorphic match operator
 (=~) :: ( Typeable a
         , RegexContext TDFA.Regex TL.Text a
@@ -78,23 +103,11 @@
 (=~~) bs rex = addCaptureNames (reCaptureNames rex) <$> matchM (reRegex rex) bs
 
 instance IsRegex RE TL.Text where
-  matchOnce     = flip (?=~)
-  matchMany     = flip (*=~)
-  makeRegexWith = \o -> compileRegexWith o . unpackE
-  makeRegex     = compileRegex . unpackE
-  regexSource   = packE . reSource
+  matchOnce             = flip (?=~)
+  matchMany             = flip (*=~)
+  makeRegexWith         = \o -> compileRegexWith o . unpackR
+  makeSearchReplaceWith = \o r t -> compileSearchReplaceWith o (unpackR r) (unpackR t)
+  regexSource           = packR . reSource
 
 -- $tutorial
--- We have a regex tutorial at <http://tutorial.regex.uk>. These API
--- docs are mainly for reference.
-
--- $toolkit
---
--- Beyond the above match operators and the regular expression type
--- below, "Text.RE" contains the toolkit for replacing captures,
--- specifying options, etc.
-
--- $re
---
--- "Text.RE.TDFA.RE" contains the toolkit specific to the 'RE' type,
--- the type generated by the gegex compiler.
+-- We have a regex tutorial at <http://tutorial.regex.uk>.
diff --git a/Text/RE/TestBench.lhs b/Text/RE/TestBench.lhs
--- a/Text/RE/TestBench.lhs
+++ b/Text/RE/TestBench.lhs
@@ -51,7 +51,11 @@
 import           Prelude.Compat
 import           Text.RE
 import           Text.RE.TestBench.Parsers
-import           Text.RE.Types.Options
+import           Text.RE.Types.Capture
+import           Text.RE.Types.REOptions
+import           Text.RE.Types.Match
+import           Text.RE.Types.Matches
+import           Text.RE.Types.Replace
 \end{code}
 
 Types
diff --git a/Text/RE/TestBench/Parsers.hs b/Text/RE/TestBench/Parsers.hs
--- a/Text/RE/TestBench/Parsers.hs
+++ b/Text/RE/TestBench/Parsers.hs
@@ -36,19 +36,19 @@
 
 
 parseInteger :: Replace a => a -> Maybe Int
-parseInteger = readMaybe . unpackE
+parseInteger = readMaybe . unpackR
 
 parseHex :: Replace a => a -> Maybe Int
-parseHex = readMaybe . ("0x"++) . unpackE
+parseHex = readMaybe . ("0x"++) . unpackR
 
 parseDouble :: Replace a => a -> Maybe Double
-parseDouble = readMaybe . unpackE
+parseDouble = readMaybe . unpackR
 
 parseString :: Replace a => a -> Maybe T.Text
-parseString = readMaybe . unpackE
+parseString = readMaybe . unpackR
 
 parseSimpleString :: Replace a => a -> Maybe T.Text
-parseSimpleString = Just . T.dropEnd 1 . T.drop 1 . textifyE
+parseSimpleString = Just . T.dropEnd 1 . T.drop 1 . textifyR
 
 date_templates, time_templates, timezone_templates,
   date_time_8601_templates, date_time_templates :: [String]
@@ -91,10 +91,10 @@
 parseDateTimeCLF = parse_time ["%d/%b/%Y:%H:%M:%S %z"]
 
 parseShortMonth :: Replace a => a -> Maybe Int
-parseShortMonth = flip HM.lookup short_month_hm . unpackE
+parseShortMonth = flip HM.lookup short_month_hm . unpackR
 
 parse_time :: (ParseTime t,Replace s) => [String] -> s -> Maybe t
-parse_time tpls = prs . unpackE
+parse_time tpls = prs . unpackR
   where
     prs s = listToMaybe $ catMaybes
       [ parseTime LC.defaultTimeLocale fmt s
@@ -111,7 +111,7 @@
 type IPV4Address = (Word8,Word8,Word8,Word8)
 
 parseIPv4Address :: Replace a => a -> Maybe IPV4Address
-parseIPv4Address = prs . words_by (=='.') . unpackE
+parseIPv4Address = prs . words_by (=='.') . unpackR
   where
     prs [a_s,b_s,c_s,d_s] = do
       a <- readMaybe a_s
@@ -137,7 +137,7 @@
   deriving (Bounded,Enum,Ord,Eq,Show)
 
 parseSeverity :: Replace a => a -> Maybe Severity
-parseSeverity = flip HM.lookup severity_hm . textifyE
+parseSeverity = flip HM.lookup severity_hm . textifyR
 
 severity_hm :: HM.HashMap T.Text Severity
 severity_hm = HM.fromList
diff --git a/Text/RE/Tools/Edit.lhs b/Text/RE/Tools/Edit.lhs
--- a/Text/RE/Tools/Edit.lhs
+++ b/Text/RE/Tools/Edit.lhs
@@ -1,14 +1,18 @@
 \begin{code}
 {-# LANGUAGE NoImplicitPrelude          #-}
 {-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE DeriveFunctor              #-}
 {-# LANGUAGE CPP                        #-}
 #if __GLASGOW_HASKELL__ >= 800
 {-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
 #endif
 
 module Text.RE.Tools.Edit
-  ( Edits(..)
+  (
+  -- * Editing
+    Edits(..)
   , Edit(..)
+  , SearchReplace(..)
   , LineEdit(..)
   , applyEdits
   , applyEdit
@@ -25,31 +29,35 @@
 import           Data.Maybe
 import           Prelude.Compat
 import           Text.RE
+import           Text.RE.Types.Capture
 import           Text.RE.Types.IsRegex
 import           Text.RE.Types.LineNo
+import           Text.RE.Types.Replace
+import           Text.RE.Types.SearchReplace
 
 
 -- | an 'Edits' script will, for each line in the file, either perform
 -- the action selected by the first RE in the list, or perform all of the
 -- actions on line, arranged as a pipeline
 data Edits m re s
-  = Select [(re,Edit m s)]
-  | Pipe   [(re,Edit m s)]
+  = Select ![Edit m re s]
+  | Pipe   ![Edit m re s]
 
 -- | each Edit action specifies how the match should be processed
-data Edit m s
-  = Template s
-  | Function Context (LineNo->Match s->Location->Capture s->m (Maybe s))
-  | LineEdit         (LineNo->Matches s->m (LineEdit s))
+data Edit m re s
+  = Template !(SearchReplace re s)
+  | Function !re REContext !(LineNo->Match s->Location->Capture s->m (Maybe s))
+  | LineEdit !re         !(LineNo->Matches s->m (LineEdit s))
 
 -- | a LineEdit is the most general action thar can be performed on a line
 -- and is the only means of deleting a line
 data LineEdit s
   = NoEdit
-  | ReplaceWith s
+  | ReplaceWith !s
   | Delete
-  deriving (Show)
+  deriving (Functor,Show)
 
+
 -- | apply an 'Edit' script to a single line
 applyEdits :: (IsRegex re s,Monad m,Functor m)
            => LineNo
@@ -67,21 +75,24 @@
 applyEdit :: (IsRegex re s,Monad m,Functor m)
           => (s->s)
           -> LineNo
-          -> re
-          -> Edit m s
+          -> Edit m re s
           -> s
           -> m (Maybe s)
-applyEdit anl lno re edit s =
+applyEdit anl lno edit s =
   case allMatches acs of
     [] -> return Nothing
     _  -> fmap Just $ case edit of
-      Template tpl   -> return $ anl $ replaceAll         tpl acs
-      Function ctx f -> anl <$> replaceAllCapturesM replaceMethods ctx (f lno) acs
-      LineEdit     g -> fromMaybe s' . applyLineEdit anl <$> g lno acs
+      Template srch_rpl -> return $ anl $ replaceAll (getTemplate srch_rpl)       acs
+      Function _ ctx f  -> anl <$> replaceAllCapturesM replaceMethods ctx (f lno) acs
+      LineEdit _     g  -> fromMaybe (anl s) . applyLineEdit anl <$> g lno        acs
   where
-    s'  = anl s
-    acs = matchMany re s
+    acs = matchMany rex s
+    rex = case edit of
+      Template srch_rpl -> getSearch srch_rpl
+      Function rex_ _ _ -> rex_
+      LineEdit rex_   _ -> rex_
 
+
 -- | apply a 'LineEdit' to a line, using the function in the first
 -- argument to append a new line to the result; Nothing should be
 -- returned if no edit is to be performed,  @Just mempty@ to
@@ -93,24 +104,24 @@
 
 select_edit_scripts :: (IsRegex re s,Monad m,Functor m)
                     => LineNo
-                    -> [(re,Edit m s)]
+                    -> [Edit m re s]
                     -> s
                     -> m s
 select_edit_scripts lno ps0 s = select ps0
   where
-    select []           = return $ appendNewlineE s
-    select ((re,es):ps) =
-      applyEdit appendNewlineE lno re es s >>= maybe (select ps) return
+    select []           = return $ appendNewlineR s
+    select (edit:edits) =
+      applyEdit appendNewlineR lno edit s >>= maybe (select edits) return
 
 pipe_edit_scripts :: (IsRegex re s,Monad m,Functor m)
                   => LineNo
-                  -> [(re,Edit m s)]
+                  -> [Edit m re s]
                   -> s
                   -> m s
-pipe_edit_scripts lno ez s0 =
-    appendNewlineE <$> foldr f (return s0) ez
+pipe_edit_scripts lno edits s0 =
+    appendNewlineR <$> foldr f (return s0) edits
   where
-    f (re,es) act = do
+    f edit act = do
       s <- act
-      fromMaybe s <$> applyEdit id lno re es s
+      fromMaybe s <$> applyEdit id lno edit s
 \end{code}
diff --git a/Text/RE/Tools/Lex.lhs b/Text/RE/Tools/Lex.lhs
--- a/Text/RE/Tools/Lex.lhs
+++ b/Text/RE/Tools/Lex.lhs
@@ -12,7 +12,10 @@
 
 import           Prelude.Compat
 import           Text.RE
+import           Text.RE.Types.Capture
 import           Text.RE.Types.IsRegex
+import           Text.RE.Types.Match
+import           Text.RE.Types.Replace
 
 
 -- | a simple regex-based scanner interpretter for prototyping
@@ -30,7 +33,7 @@
       -> [t]
 alex' mo al t_err = loop
   where
-    loop s = case lengthE s == 0 of
+    loop s = case lengthR s == 0 of
       True  -> []
       False -> choose al s
 
diff --git a/Text/RE/Tools/Sed.lhs b/Text/RE/Tools/Sed.lhs
--- a/Text/RE/Tools/Sed.lhs
+++ b/Text/RE/Tools/Sed.lhs
@@ -33,6 +33,7 @@
 import           Text.RE
 import           Text.RE.Tools.Edit
 import           Text.RE.Types.IsRegex
+import           Text.RE.Types.Replace
 
 
 -- | read a file, apply an 'Edits' script to each line it and
@@ -59,7 +60,7 @@
 sed' as lbs = do
   mconcat <$> sequence
     [ applyEdits lno as s
-        | (lno,s)<-zip [firstLine..] $ linesE lbs
+        | (lno,s)<-zip [firstLine..] $ linesR lbs
         ]
 
 read_file :: FilePath -> IO LBS.ByteString
diff --git a/Text/RE/Types.hs b/Text/RE/Types.hs
new file mode 100644
--- /dev/null
+++ b/Text/RE/Types.hs
@@ -0,0 +1,27 @@
+module Text.RE.Types
+  ( -- $collection
+    module Text.RE.Types.Capture
+  , module Text.RE.Types.CaptureID
+  , module Text.RE.Types.IsRegex
+  , module Text.RE.Types.LineNo
+  , module Text.RE.Types.Match
+  , module Text.RE.Types.Matches
+  , module Text.RE.Types.REOptions
+  , module Text.RE.Types.Replace
+  , module Text.RE.Types.SearchReplace
+  ) where
+
+import Text.RE.Types.Capture
+import Text.RE.Types.CaptureID
+import Text.RE.Types.IsRegex
+import Text.RE.Types.LineNo
+import Text.RE.Types.Match
+import Text.RE.Types.Matches
+import Text.RE.Types.REOptions
+import Text.RE.Types.Replace
+import Text.RE.Types.SearchReplace
+
+-- $collection
+--
+-- This module collects together all of the @Text.RE.Types.*@ modules
+-- to make it easy to import them all en masse.
diff --git a/Text/RE/Types/CaptureID.hs b/Text/RE/Types/CaptureID.hs
--- a/Text/RE/Types/CaptureID.hs
+++ b/Text/RE/Types/CaptureID.hs
@@ -12,8 +12,8 @@
 -- | CaptureID identifies captures, either by number
 -- (e.g., [cp|1|]) or name (e.g., [cp|foo|]).
 data CaptureID
-  = IsCaptureOrdinal CaptureOrdinal
-  | IsCaptureName    CaptureName
+  = IsCaptureOrdinal CaptureOrdinal   -- [cp|3|]
+  | IsCaptureName    CaptureName      -- [cp|y|]
   deriving (Show,Ord,Eq)
 
 -- | the dictionary for named captures stored in compiled regular
diff --git a/Text/RE/Types/IsRegex.lhs b/Text/RE/Types/IsRegex.lhs
--- a/Text/RE/Types/IsRegex.lhs
+++ b/Text/RE/Types/IsRegex.lhs
@@ -10,16 +10,31 @@
 
 import           Text.RE.Types.Match
 import           Text.RE.Types.Matches
-import           Text.RE.Types.Options
+import           Text.RE.Types.REOptions
 import           Text.RE.Types.Replace
+import           Text.RE.Types.SearchReplace
 \end{code}
 
 
 \begin{code}
+-- | the 'IsRegex' class allows tools to be written that will work with
+-- regex back end text type supported by the back end
 class Replace s => IsRegex re s where
-  matchOnce     :: re -> s -> Match s
-  matchMany     :: re -> s -> Matches s
-  makeRegex     :: (Functor m,Monad m) => s -> m re
-  makeRegexWith :: (Functor m,Monad m) => SimpleRegexOptions -> s -> m re
-  regexSource   :: re -> s
+  -- | finding the first match
+  matchOnce             :: re -> s -> Match s
+  -- | finding all matches
+  matchMany             :: re -> s -> Matches s
+  -- | compiling an RE, failing if the RE is not well formed
+  makeRegex             :: (Functor m,Monad m) => s -> m re
+  -- | comiling an RE, specifying the 'SimpleREOptions'
+  makeRegexWith         :: (Functor m,Monad m) => SimpleREOptions -> s -> m re
+  -- | compiling a 'SearchReplace' template from the RE text and the template Text, failing if they are not well formed
+  makeSearchReplace     :: (Functor m,Monad m,IsRegex re s) => s -> s -> m (SearchReplace re s)
+  -- | compiling a 'SearchReplace' template specifing the 'SimpleREOptions' for the RE
+  makeSearchReplaceWith :: (Functor m,Monad m,IsRegex re s) => SimpleREOptions -> s -> s -> m (SearchReplace re s)
+  -- | extract the text of the RE from the RE
+  regexSource           :: re -> s
+
+  makeRegex         = makeRegexWith         minBound
+  makeSearchReplace = makeSearchReplaceWith minBound
 \end{code}
diff --git a/Text/RE/Types/LineNo.hs b/Text/RE/Types/LineNo.hs
--- a/Text/RE/Types/LineNo.hs
+++ b/Text/RE/Types/LineNo.hs
@@ -3,16 +3,19 @@
 module Text.RE.Types.LineNo where
 
 
+-- | our line numbers are of the proper zero-based kind
 newtype LineNo =
     ZeroBasedLineNo { getZeroBasedLineNo :: Int }
   deriving (Show,Enum)
 
-
+-- | the first line in a file
 firstLine :: LineNo
 firstLine = ZeroBasedLineNo 0
 
+-- | extract a conventional 1-based line number
 getLineNo :: LineNo -> Int
-getLineNo = (+1) . getZeroBasedLineNo
+getLineNo = succ . getZeroBasedLineNo
 
+-- | inject a conventional 1-based line number
 lineNo :: Int -> LineNo
-lineNo = ZeroBasedLineNo . (\x->x-1)
+lineNo = ZeroBasedLineNo . pred
diff --git a/Text/RE/Types/Match.lhs b/Text/RE/Types/Match.lhs
--- a/Text/RE/Types/Match.lhs
+++ b/Text/RE/Types/Match.lhs
@@ -157,7 +157,8 @@
 
 
 \begin{code}
--- | for matching just the first RE against the source text
+-- | this instance hooks 'Match' into regex-base: regex consumers need
+-- not worry about any of this
 instance
     ( RegexContext regex source (AllTextSubmatches (Array Int) (source,(Int,Int)))
     , RegexLike    regex source
diff --git a/Text/RE/Types/Matches.lhs b/Text/RE/Types/Matches.lhs
--- a/Text/RE/Types/Matches.lhs
+++ b/Text/RE/Types/Matches.lhs
@@ -25,14 +25,13 @@
 \end{code}
 
 
-
 \begin{code}
 -- | the result type to use when every match is needed, not just the
 -- first match of the RE against the source
 data Matches a =
   Matches
     { matchesSource :: !a          -- ^ the source text being matched
-    , allMatches    :: ![Match a]  -- ^ all captures found, left to right
+    , allMatches    :: ![Match a]  -- ^ all 'Match' instances found, left to right
     }
   deriving (Show,Eq,Typeable)
 \end{code}
@@ -66,9 +65,9 @@
     c0 = IsCaptureOrdinal $ CaptureOrdinal 0
 \end{code}
 
-
 \begin{code}
--- | for matching all REs against the source text
+-- | this instance hooks 'Matches' into regex-base: regex consumers need
+-- not worry about any of this
 instance
     ( RegexContext regex source [MatchText source]
     , RegexLike    regex source
diff --git a/Text/RE/Types/Options.lhs b/Text/RE/Types/Options.lhs
deleted file mode 100644
--- a/Text/RE/Types/Options.lhs
+++ /dev/null
@@ -1,69 +0,0 @@
-\begin{code}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE TemplateHaskell            #-}
-{-# LANGUAGE QuasiQuotes                #-}
-{-# LANGUAGE MultiParamTypeClasses      #-}
-{-# LANGUAGE FunctionalDependencies     #-}
-
-module Text.RE.Types.Options where
-
-import           Data.Hashable
-import qualified Data.HashMap.Strict        as HM
-import           Data.String
-import           Language.Haskell.TH
-import           Language.Haskell.TH.Syntax
-\end{code}
-
-\begin{code}
-data Options_ r c e =
-  Options
-    { optionsMacs :: !(Macros r)
-    , optionsComp :: !c
-    , optionsExec :: !e
-    }
-  deriving (Show)
-\end{code}
-
-\begin{code}
-class IsOption o r c e |
-    e -> r, c -> e , e -> c, r -> c, c -> r, r -> e where
-  makeOptions :: o -> Options_ r c e
-\end{code}
-
-\begin{code}
-newtype MacroID =
-    MacroID { getMacroID :: String }
-  deriving (IsString,Ord,Eq,Show)
-\end{code}
-
-\begin{code}
-instance Hashable MacroID where
-  hashWithSalt i = hashWithSalt i . getMacroID
-\end{code}
-
-\begin{code}
-type Macros r = HM.HashMap MacroID r
-\end{code}
-
-\begin{code}
-emptyMacros :: Macros r
-emptyMacros = HM.empty
-\end{code}
-
-\begin{code}
-data SimpleRegexOptions
-  = MultilineSensitive
-  | MultilineInsensitive
-  | BlockSensitive
-  | BlockInsensitive
-  deriving (Bounded,Enum,Eq,Ord,Show)
-\end{code}
-
-\begin{code}
-instance Lift SimpleRegexOptions where
-  lift sro = case sro of
-    MultilineSensitive    -> conE 'MultilineSensitive
-    MultilineInsensitive  -> conE 'MultilineInsensitive
-    BlockSensitive        -> conE 'BlockSensitive
-    BlockInsensitive      -> conE 'BlockInsensitive
-\end{code}
diff --git a/Text/RE/Types/REOptions.lhs b/Text/RE/Types/REOptions.lhs
new file mode 100644
--- /dev/null
+++ b/Text/RE/Types/REOptions.lhs
@@ -0,0 +1,94 @@
+\begin{code}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE FunctionalDependencies     #-}
+{-# LANGUAGE CPP                        #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# LANGUAGE TemplateHaskellQuotes      #-}
+#else
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE TemplateHaskell            #-}
+#endif
+
+module Text.RE.Types.REOptions where
+
+import           Data.Hashable
+import qualified Data.HashMap.Strict        as HM
+import           Data.String
+import           Language.Haskell.TH
+import           Language.Haskell.TH.Syntax
+\end{code}
+
+\begin{code}
+-- | the default API uses these simple, universal RE options,
+-- which get auto-converted into the apropriate 'REOptions_' actually
+-- as apropriate the chosen back end
+data SimpleREOptions
+  = MultilineSensitive        -- ^ case-sensitive with ^ and $ matching the start and end of a line
+  | MultilineInsensitive      -- ^ case-insensitive with ^ and $ matsh the start and end of a line
+  | BlockSensitive            -- ^ case-sensitive with ^ and $ matching the start and end of the input text
+  | BlockInsensitive          -- ^ case-insensitive with ^ and $ matching the start and end of the input text
+  deriving (Bounded,Enum,Eq,Ord,Show)
+\end{code}
+
+\begin{code}
+-- | we need to use this in the quasi quoters to specify @SimpleREOptions@
+-- selected by the quasi quoter
+instance Lift SimpleREOptions where
+  lift sro = case sro of
+    MultilineSensitive    -> conE 'MultilineSensitive
+    MultilineInsensitive  -> conE 'MultilineInsensitive
+    BlockSensitive        -> conE 'BlockSensitive
+    BlockInsensitive      -> conE 'BlockInsensitive
+\end{code}
+
+\begin{code}
+-- | the general options for an RE are dependent on which back end is
+-- being used and are parameterised over the @RE@ type for the back end,
+-- and its @CompOption@ and @ExecOption@ types (the compile-time and
+-- execution time options, respectively); each back end will define an
+-- @REOptions@ type that fills out these three type parameters with the
+-- apropriate types
+data REOptions_ r c e =
+  REOptions
+    { optionsMacs :: !(Macros r)    -- ^ the available TestBench RE macros
+    , optionsComp :: !c             -- ^ the back end compile-time options
+    , optionsExec :: !e             -- ^ the back end execution-time options
+    }
+  deriving (Show)
+\end{code}
+
+\begin{code}
+-- | a number of types can be used to encode @REOptions@, each of which
+-- can be made a member of this class.
+class IsOption o r c e |
+    e -> r, c -> e , e -> c, r -> c, c -> r, r -> e where
+  -- | convert the @o@ type into an @REOptions r c e@
+  makeREOptions :: o -> REOptions_ r c e
+\end{code}
+
+\begin{code}
+-- | @MacroID@ is just a wrapped @String@ type with an @IsString@
+-- instance
+newtype MacroID =
+    MacroID { getMacroID :: String }
+  deriving (IsString,Ord,Eq,Show)
+\end{code}
+
+\begin{code}
+-- | @MacroID@ is used with @HM.HashMap@ to build macro lookup tables
+instance Hashable MacroID where
+  hashWithSalt i = hashWithSalt i . getMacroID
+\end{code}
+
+\begin{code}
+-- | our macro tables are parameterised over the backend @RE@ type and
+-- and just associate each @MacroID@ with an @RE@
+type Macros r = HM.HashMap MacroID r
+\end{code}
+
+\begin{code}
+-- | a macro table containing no entries
+emptyMacros :: Macros r
+emptyMacros = HM.empty
+\end{code}
diff --git a/Text/RE/Types/Replace.lhs b/Text/RE/Types/Replace.lhs
--- a/Text/RE/Types/Replace.lhs
+++ b/Text/RE/Types/Replace.lhs
@@ -10,7 +10,7 @@
   ( Replace(..)
   , ReplaceMethods(..)
   , replaceMethods
-  , Context(..)
+  , REContext(..)
   , Location(..)
   , isTopLocation
   , replace
@@ -23,6 +23,7 @@
   , replaceCapturesM
   , expandMacros
   , expandMacros'
+  , templateCaptures
   ) where
 
 import           Control.Applicative
@@ -40,50 +41,50 @@
 import qualified Data.Text.Encoding             as TE
 import qualified Data.Text.Lazy                 as LT
 import           Prelude.Compat
-import           Text.Heredoc
 import           Text.RE.Types.Capture
 import           Text.RE.Types.CaptureID
 import           Text.RE.Types.Match
 import           Text.RE.Types.Matches
-import           Text.RE.Types.Options
+import           Text.RE.Types.REOptions
 import           Text.Read
 import           Text.Regex.TDFA
 import           Text.Regex.TDFA.Text()
 import           Text.Regex.TDFA.Text.Lazy()
 \end{code}
 
+
 \begin{code}
 -- | Replace provides the missing methods needed to replace the matched
--- text; lengthE is the minimum implementation
+-- text; lengthR is the minimum implementation
 class (Show a,Eq a,Ord a,Extract a,Monoid a) => Replace a where
   -- | length function for a
-  lengthE        :: a -> Int
+  lengthR        :: a -> Int
   -- | inject String into a
-  packE          :: String -> a
+  packR          :: String -> a
   -- | project a onto a String
-  unpackE        :: a -> String
+  unpackR        :: a -> String
   -- | inject into Text
-  textifyE       :: a -> T.Text
+  textifyR       :: a -> T.Text
   -- | project Text onto a
-  detextifyE     :: T.Text -> a
+  detextifyR     :: T.Text -> a
   -- | split into lines
-  linesE         :: a -> [a]
+  linesR         :: a -> [a]
   -- | concatenate a list of lines
-  unlinesE       :: [a] -> a
+  unlinesR       :: [a] -> a
   -- | append a newline
-  appendNewlineE :: a -> a
+  appendNewlineR :: a -> a
   -- | apply a substitution function to a Capture
-  substE         :: (a->a) -> Capture a -> a
+  substR         :: (a->a) -> Capture a -> a
   -- | convert a template containing $0, $1, etc., in the first
   -- argument, into a 'phi' replacement function for use with
   -- replaceAllCaptures and replaceCaptures
-  parseTemplateE :: a -> Match a -> Location -> Capture a -> Maybe a
+  parseTemplateR :: a -> Match a -> Location -> Capture a -> Maybe a
 
-  textifyE       = T.pack . unpackE
-  detextifyE     = packE  . T.unpack
-  appendNewlineE = (<> packE "\n")
+  textifyR       = T.pack . unpackR
+  detextifyR     = packR  . T.unpack
+  appendNewlineR = (<> packR "\n")
 
-  substE f m@Capture{..} =
+  substR f m@Capture{..} =
     capturePrefix m <> f capturedText <> captureSuffix m
 \end{code}
 
@@ -100,14 +101,14 @@
 replaceMethods :: Replace a => ReplaceMethods a
 replaceMethods =
   ReplaceMethods
-    { methodLength = lengthE
-    , methodSubst  = substE
+    { methodLength = lengthR
+    , methodSubst  = substR
     }
 \end{code}
 
 \begin{code}
--- | @Context@ specifies which contexts the substitutions should be applied
-data Context
+-- | @REContext@ specifies which contexts the substitutions should be applied
+data REContext
   = TOP   -- ^ substitutions should be applied to the top-level only,
           -- the text that matched the whole RE
   | SUB   -- ^ substitutions should only be applied to the text
@@ -147,7 +148,7 @@
            => a
            -> Matches a
            -> a
-replaceAll tpl ac = replaceAllCaptures TOP (parseTemplateE tpl) ac
+replaceAll tpl ac = replaceAllCaptures TOP (parseTemplateR tpl) ac
 \end{code}
 
 \begin{code}
@@ -155,7 +156,7 @@
 -- context and returns the same replacement text as the _phi_phi
 -- context.
 replaceAllCaptures :: Replace a
-                   => Context
+                   => REContext
                    -> (Match a->Location->Capture a->Maybe a)
                    -> Matches a
                    -> a
@@ -170,7 +171,7 @@
 -- Replace methods through the ReplaceMethods argument
 replaceAllCaptures_ :: Extract a
                     => ReplaceMethods a
-                    -> Context
+                    -> REContext
                     -> (Match a->Location->Capture a->Maybe a)
                     -> Matches a
                     -> a
@@ -183,7 +184,7 @@
 -- replaceAllCaptures_
 replaceAllCapturesM :: (Extract a,Monad m)
                     => ReplaceMethods a
-                    -> Context
+                    -> REContext
                     -> (Match a->Location->Capture a->m (Maybe a))
                     -> Matches a
                     -> m a
@@ -229,10 +230,10 @@
 -- | replace with a template containing $0 for whole text,
 -- $1 for first capture, etc.
 replace :: Replace a
-        => Match a
-        -> a
+        => a
+        -> Match a
         -> a
-replace c tpl = replaceCaptures TOP (parseTemplateE tpl) c
+replace tpl c = replaceCaptures TOP (parseTemplateR tpl) c
 \end{code}
 
 \begin{code}
@@ -240,7 +241,7 @@
 -- context and returns the same replacement text as the _phi_phi
 -- context.
 replaceCaptures :: Replace a
-                 => Context
+                 => REContext
                  -> (Match a->Location->Capture a->Maybe a)
                  -> Match a
                  -> a
@@ -252,7 +253,7 @@
 -- through the ReplaceMethods argument
 replaceCaptures_ :: Extract a
                  => ReplaceMethods a
-                 -> Context
+                 -> REContext
                  -> (Match a->Location->Capture a->Maybe a)
                  -> Match a
                  -> a
@@ -265,7 +266,7 @@
 -- replaceCaptures_
 replaceCapturesM :: (Monad m,Extract a)
                  => ReplaceMethods a
-                 -> Context
+                 -> REContext
                  -> (Match a->Location->Capture a->m (Maybe a))
                  -> Match a
                  -> m a
@@ -319,67 +320,67 @@
 -- the Replace instances
 
 instance Replace [Char] where
-  lengthE         = length
-  packE           = id
-  unpackE         = id
-  textifyE        = T.pack
-  detextifyE      = T.unpack
-  linesE          = lines
-  unlinesE        = unlines
-  appendNewlineE  = (<>"\n")
-  parseTemplateE  = parseTemplateE' id
+  lengthR         = length
+  packR           = id
+  unpackR         = id
+  textifyR        = T.pack
+  detextifyR      = T.unpack
+  linesR          = lines
+  unlinesR        = unlines
+  appendNewlineR  = (<>"\n")
+  parseTemplateR  = parseTemplateR' id
 
 instance Replace B.ByteString where
-  lengthE         = B.length
-  packE           = B.pack
-  unpackE         = B.unpack
-  textifyE        = TE.decodeUtf8
-  detextifyE      = TE.encodeUtf8
-  linesE          = B.lines
-  unlinesE        = B.unlines
-  appendNewlineE  = (<>"\n")
-  parseTemplateE  = parseTemplateE' B.unpack
+  lengthR         = B.length
+  packR           = B.pack
+  unpackR         = B.unpack
+  textifyR        = TE.decodeUtf8
+  detextifyR      = TE.encodeUtf8
+  linesR          = B.lines
+  unlinesR        = B.unlines
+  appendNewlineR  = (<>"\n")
+  parseTemplateR  = parseTemplateR' B.unpack
 
 instance Replace LBS.ByteString where
-  lengthE         = fromEnum . LBS.length
-  packE           = LBS.pack
-  unpackE         = LBS.unpack
-  textifyE        = TE.decodeUtf8  . LBS.toStrict
-  linesE          = LBS.lines
-  unlinesE        = LBS.unlines
-  detextifyE      = LBS.fromStrict . TE.encodeUtf8
-  appendNewlineE  = (<>"\n")
-  parseTemplateE  = parseTemplateE' LBS.unpack
+  lengthR         = fromEnum . LBS.length
+  packR           = LBS.pack
+  unpackR         = LBS.unpack
+  textifyR        = TE.decodeUtf8  . LBS.toStrict
+  linesR          = LBS.lines
+  unlinesR        = LBS.unlines
+  detextifyR      = LBS.fromStrict . TE.encodeUtf8
+  appendNewlineR  = (<>"\n")
+  parseTemplateR  = parseTemplateR' LBS.unpack
 
 instance Replace (S.Seq Char) where
-  lengthE         = S.length
-  packE           = S.fromList
-  unpackE         = F.toList
-  linesE          = map packE . lines . unpackE
-  unlinesE        = packE . unlines . map unpackE
-  parseTemplateE  = parseTemplateE' F.toList
+  lengthR         = S.length
+  packR           = S.fromList
+  unpackR         = F.toList
+  linesR          = map packR . lines . unpackR
+  unlinesR        = packR . unlines . map unpackR
+  parseTemplateR  = parseTemplateR' F.toList
 
 instance Replace T.Text where
-  lengthE         = T.length
-  packE           = T.pack
-  unpackE         = T.unpack
-  textifyE        = id
-  detextifyE      = id
-  linesE          = T.lines
-  unlinesE        = T.unlines
-  appendNewlineE  = (<>"\n")
-  parseTemplateE  = parseTemplateE' T.unpack
+  lengthR         = T.length
+  packR           = T.pack
+  unpackR         = T.unpack
+  textifyR        = id
+  detextifyR      = id
+  linesR          = T.lines
+  unlinesR        = T.unlines
+  appendNewlineR  = (<>"\n")
+  parseTemplateR  = parseTemplateR' T.unpack
 
 instance Replace LT.Text where
-  lengthE         = fromEnum . LT.length
-  packE           = LT.pack
-  unpackE         = LT.unpack
-  textifyE        = LT.toStrict
-  detextifyE      = LT.fromStrict
-  linesE          = LT.lines
-  unlinesE        = LT.unlines
-  appendNewlineE  = (<>"\n")
-  parseTemplateE  = parseTemplateE' LT.unpack
+  lengthR         = fromEnum . LT.length
+  packR           = LT.pack
+  unpackR         = LT.unpack
+  textifyR        = LT.toStrict
+  detextifyR      = LT.fromStrict
+  linesR          = LT.lines
+  unlinesR        = LT.unlines
+  appendNewlineR  = (<>"\n")
+  parseTemplateR  = parseTemplateR' LT.unpack
 \end{code}
 
 \begin{code}
@@ -399,7 +400,7 @@
 expandMacros' :: (MacroID->Maybe String) -> String -> String
 expandMacros' lu = fixpoint e_m
   where
-    e_m re_s = replaceAllCaptures TOP phi $ re_s $=~ [here|@(@|\{([^{}]+)\})|]
+    e_m re_s = replaceAllCaptures TOP phi $ re_s $=~ "@(@|\\{([^{}]+)\\})"
       where
         phi mtch _ cap = case txt == "@@" of
             True  -> Just   "@"
@@ -420,7 +421,12 @@
 \end{code}
 
 \begin{code}
-parseTemplateE' :: ( Replace a
+-- | parse the replacement template in second argument, substititing
+-- the capture references with corresponding captures from the Match
+-- in the third argument (the result of a single match of the RE
+-- against the input text to be matched); Nothing is returned if the
+-- inputs are not well formed (currently all inputs are well formed)
+parseTemplateR' :: ( Replace a
                    , RegexContext Regex a (Matches a)
                    , RegexMaker   Regex CompOption ExecOption String
                    )
@@ -430,27 +436,56 @@
                    -> Location
                    -> Capture a
                    -> Maybe a
-parseTemplateE' unpack tpl mtch _ _ =
-    Just $ replaceAllCaptures TOP phi $
-      tpl $=~ [here|\$(\$|[0-9]|\{([^{}]+)\})|]
+parseTemplateR' unpack tpl mtch _ _ =
+    Just $ replaceAllCaptures TOP phi $ scan_template tpl
   where
-    phi t_mtch _ _ = case t_mtch !$? c2 of
-      Just cap -> case readMaybe stg of
-          Nothing -> this $ IsCaptureName    $ CaptureName $ T.pack stg
-          Just cn -> this $ IsCaptureOrdinal $ CaptureOrdinal cn
-        where
-          stg = unpack $ capturedText cap
-      Nothing -> case s == "$" of
-        True  -> Just t
-        False -> this $ IsCaptureOrdinal $ CaptureOrdinal $ read s
-      where
-        s = unpack t
-        t = capturedText $ capture c1 t_mtch
+    phi t_mtch _ _ = either Just this $ parse_template_capture unpack t_mtch
 
-        this cid = capturedText <$> mtch !$? cid
+    this cid       = capturedText <$> mtch !$? cid
 
+-- | list all of the CaptureID references in the replace template in
+-- the second argument
+templateCaptures :: ( Replace a
+                    , RegexContext Regex a (Matches a)
+                    , RegexMaker   Regex CompOption ExecOption String
+                    )
+                 => (a->String)
+                 -> a
+                 -> [CaptureID]
+templateCaptures unpack tpl =
+    [ cid
+      | mtch <- allMatches $ scan_template tpl
+      , Right cid <- [parse_template_capture unpack mtch]
+      ]
+
+-- | parse a Match generated by acan_template, returning @Left "$")
+-- iff the capture reference is an escaped @$@ (i.e., @$$@)
+parse_template_capture :: (a->String) -> Match a -> Either a CaptureID
+parse_template_capture unpack t_mtch = case t_mtch !$? c2 of
+  Just cap -> case readMaybe stg of
+      Nothing -> Right $ IsCaptureName    $ CaptureName $ T.pack stg
+      Just cn -> Right $ IsCaptureOrdinal $ CaptureOrdinal cn
+    where
+      stg = unpack $ capturedText cap
+  Nothing -> case s == "$" of
+    True  -> Left t
+    False -> Right $ IsCaptureOrdinal $ CaptureOrdinal $ read s
+  where
+    s = unpack t
+    t = capturedText $ capture c1 t_mtch
+
     c1 = IsCaptureOrdinal $ CaptureOrdinal 1
     c2 = IsCaptureOrdinal $ CaptureOrdinal 2
+
+-- | scan a replacement template, returning a Match for each capture
+-- reference in the template (like $1, ${foo})
+scan_template :: ( Replace a
+                 , RegexContext Regex a (Matches a)
+                 , RegexMaker   Regex CompOption ExecOption String
+                 )
+              => a
+              -> Matches a
+scan_template tpl = tpl $=~ "\\$(\\$|[0-9]|\\{([^{}]+)\\})"
 \end{code}
 
 \begin{code}
@@ -467,5 +502,4 @@
          )
       => source -> String -> target
 ($=~) = (=~)
-
 \end{code}
diff --git a/Text/RE/Types/SearchReplace.hs b/Text/RE/Types/SearchReplace.hs
new file mode 100644
--- /dev/null
+++ b/Text/RE/Types/SearchReplace.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE NoImplicitPrelude          #-}
+{-# LANGUAGE RecordWildCards            #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE TypeSynonymInstances       #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE CPP                        #-}
+#if __GLASGOW_HASKELL__ >= 800
+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
+{-# LANGUAGE TemplateHaskellQuotes      #-}
+#else
+{-# LANGUAGE QuasiQuotes                #-}
+{-# LANGUAGE TemplateHaskell            #-}
+#endif
+{-# OPTIONS_GHC -fno-warn-orphans       #-}
+
+module Text.RE.Types.SearchReplace
+  ( SearchReplace(..)
+  ) where
+
+import           Prelude.Compat
+
+
+-- | contains a compiled RE and replacement template
+data SearchReplace re s =
+  SearchReplace
+    { getSearch   :: !re    -- ^ the RE
+    , getTemplate :: !s     -- ^ the replacement template
+    }
+  deriving (Show)
+
+instance Functor (SearchReplace re) where
+  fmap f (SearchReplace re x) = SearchReplace re (f x)
diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,5 +1,16 @@
 -*-change-log-*-
 
+0.9.0.0 Chris Dornan <chris.dornan@irisconnect.co.uk> 2017-03-23
+  * Flip the order of the arguments to replace (#78)
+  * Add type-safe replacement templates (#60)
+  * Finish tidying up the API (#80)
+  * Make `regex` compatible w/ TH-less GHCs (#70)
+  * Declare extensions the compiler must support (#83)
+  * Fix curl for AppVeyor build (#79)
+  * Fix AppVeyor badge (#81)
+  * Remove QQ from code coverage stats (#82)
+  * Rename Options, Context and Replace methods (#84)
+
 0.8.0.0 Chris Dornan <chris.dornan@irisconnect.co.uk> 2017-03-16
   * Tidy up the API after recent reorganization (#76)
 
diff --git a/regex.cabal b/regex.cabal
--- a/regex.cabal
+++ b/regex.cabal
@@ -1,5 +1,5 @@
 Name:                   regex
-Version:                0.8.0.0
+Version:                0.9.0.0
 Synopsis:               Toolkit for regex-base
 Description:            A Regular Expression Toolkit for regex-base with
                         Compile-time checking of RE syntax, data types for
@@ -22,7 +22,7 @@
     README.markdown
     changelog
 
-Cabal-Version:          >= 1.16
+Cabal-Version:          >= 1.10
 
 Source-Repository head
     type:               git
@@ -31,7 +31,7 @@
 Source-Repository this
     Type:               git
     Location:           https://github.com/iconnect/regex.git
-    Tag:                0.8.0.0
+    Tag:                0.9.0.0
 
 
 
@@ -45,6 +45,8 @@
       Text.RE.Internal.NamedCaptures
       Text.RE.Internal.PreludeMacros
       Text.RE.Internal.QQ
+      Text.RE.SearchReplace
+      Text.RE.Summa
       Text.RE.TDFA
       Text.RE.TDFA.ByteString
       Text.RE.TDFA.ByteString.Lazy
@@ -60,16 +62,44 @@
       Text.RE.Tools.Grep
       Text.RE.Tools.Lex
       Text.RE.Tools.Sed
+      Text.RE.Types
       Text.RE.Types.Capture
       Text.RE.Types.CaptureID
       Text.RE.Types.IsRegex
       Text.RE.Types.LineNo
       Text.RE.Types.Match
       Text.RE.Types.Matches
-      Text.RE.Types.Options
+      Text.RE.Types.REOptions
       Text.RE.Types.Replace
+      Text.RE.Types.SearchReplace
 
     Default-Language:   Haskell2010
+
+    Other-Extensions:
+      AllowAmbiguousTypes
+      CPP
+      DeriveDataTypeable
+      DeriveGeneric
+      ExistentialQuantification
+      FlexibleContexts
+      FlexibleInstances
+      FunctionalDependencies
+      GeneralizedNewtypeDeriving
+      MultiParamTypeClasses
+      NoImplicitPrelude
+      OverloadedStrings
+      QuasiQuotes
+      RecordWildCards
+      ScopedTypeVariables
+      TemplateHaskell
+      TypeSynonymInstances
+      UndecidableInstances
+
+    if !impl(ghc >= 8.0)
+      Other-Extensions: TemplateHaskell
+    else
+      Other-Extensions: TemplateHaskellQuotes
+
     GHC-Options:
       -Wall
       -fwarn-tabs
@@ -82,7 +112,6 @@
       , bytestring           >= 0.10.2.0
       , containers           >= 0.4
       , hashable             >= 1.2.3.3
-      , heredoc              >= 0.2.0.0
       , regex-base           >= 0.93.2
       , regex-tdfa           >= 1.2.0
       , regex-tdfa-text      >= 1.0.0.3
