diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
 # Revision history for replace-megaparsec
 
+## 1.3.0.0 -- 2020-03-06
+
+`sepCap` won't throw.
+
+Don't throw an exception on an unreachable error case, just bottom.
+Remove type constraints for `Exception`.
+
+## 1.2.1.0 -- 2020-01-01
+
+Allow any error parameter, not just `Void`.
+
 ## 1.2.0.0 -- 2019-10-31
 
 Benchmark improvements
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -64,6 +64,8 @@
   string-to-number conversion function fails. A typed parser will perform both
   the pattern match and the conversion, so it will never be in that situation.
 
+  [Parse, don't validate.](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
+
 * Regular expressions are only able to pattern-match
   [regular](https://en.wikipedia.org/wiki/Chomsky_hierarchy#The_hierarchy)
   grammers.
@@ -87,6 +89,7 @@
 The examples depend on these imports.
 
 ```haskell
+import Data.Void
 import Replace.Megaparsec
 import Text.Megaparsec
 import Text.Megaparsec.Char
@@ -185,7 +188,8 @@
 Replace all carriage-return-newline instances with newline.
 
 ```haskell
-streamEdit (chunk "\r\n") (const "\n") "1\r\n2\r\n"
+let crnl = chunk "\r\n" :: Parsec Void String String
+streamEdit crnl (const "\n") "1\r\n2\r\n"
 ```
 ```haskell
 "1\n2\n"
@@ -196,7 +200,8 @@
 Replace alphabetic characters with the next character in the alphabet.
 
 ```haskell
-streamEdit (some letterChar) (fmap succ) "HAL 9000"
+let somelet = some letterChar :: Parsec Void String String
+streamEdit somelet (fmap succ) "HAL 9000"
 ```
 ```haskell
 "IBM 9000"
@@ -225,7 +230,8 @@
 
 ```haskell
 import System.Environment
-streamEditT (char '{' *> manyTill anySingle (char '}')) getEnv "- {HOME} -"
+let bracevar = char '{' *> manyTill anySingle (char '}') :: ParsecT Void String IO String
+streamEditT bracevar getEnv "- {HOME} -"
 ```
 ```haskell
 "- /home/jbrock -"
@@ -270,18 +276,19 @@
 #!/usr/bin/env stack
 {- stack
   script
-  --resolver nightly-2019-09-13
+  --resolver lts-15
   --package megaparsec
   --package replace-megaparsec
 -}
 -- https://docs.haskellstack.org/en/stable/GUIDE/#script-interpreter
 
+import Data.Void
 import Text.Megaparsec
 import Text.Megaparsec.Char
 import Text.Megaparsec.Char.Lexer
 import Replace.Megaparsec
 
-main = interact $ streamEdit decimal (show . (*2))
+main = interact $ streamEdit (decimal :: Parsec Void String Int) (show . (*2))
 ```
 
 If you have
@@ -356,27 +363,28 @@
 
 | Program                                           | dense     | sparse   |
 | :---                                              |      ---: |     ---: |
-| Python `re.sub`¹                                  | 89.23ms   | 23.98ms  |
-| Perl `s///ge`²                                    | 180.65ms  | 5.60ms   |
-| [`Replace.Megaparsec.streamEdit`][m] `String`     | 454.95ms  | 375.04ms |
+| [Python 3.7.4 `re.sub`][sub] *repl* function      | 89.23ms   | 23.98ms  |
+| [Perl 5 `s///ge`][s]                              | 180.65ms  | 5.02ms   |
+| [`Replace.Megaparsec.streamEdit`][m] `String`     | 441.94ms  | 375.04ms |
 | [`Replace.Megaparsec.streamEdit`][m] `ByteString` | 529.99ms  | 73.76ms  |
 | [`Replace.Megaparsec.streamEdit`][m] `Text`       | 547.47ms  | 139.21ms |
 | [`Replace.Attoparsec.ByteString.streamEdit`][ab]  | 394.12ms  | 41.13ms  |
 | [`Replace.Attoparsec.Text.streamEdit`][at]        | 515.26ms  | 46.10ms  |
 | [`Text.Regex.Applicative.replace`][ra] `String`   | 1083.98ms | 646.40ms |
-| [`Text.Regex.PCRE.Heavy.gsub`][ph] `Text`         | ⊥³        | 14.76ms  |
-
-¹ Python 3.7.4
-
-² This is perl 5, version 28, subversion 2 (v5.28.2) built for x86_64-linux-thread-multi
-
-³ Does not finish.
+| [`Text.Regex.PCRE.Heavy.gsub`][ph] `Text`         | > 10min   | 14.29ms  |
+| [`Control.Lens.Regex.ByteString.match`][lb]       | > 10min   | 4.27ms   |
+| [`Control.Lens.Regex.Text.match`][lt]             | > 10min   | 14.74ms  |
 
+[sub]: https://docs.python.org/3/library/re.html#re.sub
+[s]: https://perldoc.perl.org/functions/s.html
 [m]: https://hackage.haskell.org/package/replace-megaparsec/docs/Replace-Megaparsec.html#v:streamEdit
 [ab]: https://hackage.haskell.org/package/replace-attoparsec/docs/Replace-Attoparsec-ByteString.html#v:streamEdit
 [at]: https://hackage.haskell.org/package/replace-attoparsec/docs/Replace-Attoparsec-Text.html#v:streamEdit
 [ra]: http://hackage.haskell.org/package/regex-applicative/docs/Text-Regex-Applicative.html#v:replace
-[ph]: http://hackage.haskell.org/package/pcre-heavy/docs/Text-Regex-PCRE-Heavy.html
+[ss]: http://hackage.haskell.org/package/stringsearch/docs/Data-ByteString-Search.html#v:replace
+[ph]: http://hackage.haskell.org/package/pcre-heavy/docs/Text-Regex-PCRE-Heavy.html#v:gsub
+[lb]: https://hackage.haskell.org/package/lens-regex-pcre/docs/Control-Lens-Regex-ByteString.html#v:match
+[lt]: https://hackage.haskell.org/package/lens-regex-pcre/docs/Control-Lens-Regex-Text.html#v:match
 
 
 # Hypothetically Asked Questions
@@ -390,18 +398,21 @@
 
 2. *Is this a good idea?*
 
-   You may have heard it suggested that monadic parsers are better when
+   You may have
+   [heard it suggested](https://stackoverflow.com/questions/57667534/how-can-i-use-a-parser-in-haskell-to-find-the-locations-of-some-substrings-in-a/57712672#comment101804063_57667534)
+   that monadic parsers are better for pattern-matching when
    the input stream is mostly signal, and regular expressions are better
    when the input stream is mostly noise.
 
-   The premise of this library is:
-   that sentiment is outdated; monadic parsers are great for finding
-   small patterns in a stream of otherwise uninteresting text; and the
-   reluctance to forego the speedup opportunities afforded by restricting
-   ourselves to regular grammars is an old superstition about
+   The premise of this library is that monadic parsers are great for finding
+   small signal patterns in a stream of otherwise noisy text.
+
+   There is a reluctance to forego the speedup opportunities afforded by restricting
+   ourselves to regular grammars, but this is an old superstition about
    opportunities which
    [remain mostly unexploited anyway](https://swtch.com/~rsc/regexp/regexp1.html).
-   The performance compromise of allowing stack memory allocation (a.k.a pushdown
-   automata, a.k.a context-free grammar) was once considered
-   [controversial for *general-purpose* programming languages](https://vanemden.wordpress.com/2014/06/18/how-recursion-got-into-programming-a-comedy-of-errors-3/). I think we
+   The performance compromise of allowing stack memory allocation (a.k.a. pushdown
+   automata, a.k.a. context-free grammar) was once considered
+   [controversial for *general-purpose* programming languages](https://vanemden.wordpress.com/2014/06/18/how-recursion-got-into-programming-a-comedy-of-errors-3/).
+   I think we
    can now resolve that controversy the same way for pattern matching languages.
diff --git a/replace-megaparsec.cabal b/replace-megaparsec.cabal
--- a/replace-megaparsec.cabal
+++ b/replace-megaparsec.cabal
@@ -1,5 +1,5 @@
 name:                replace-megaparsec
-version:             1.2.1.0
+version:             1.3.0.0
 cabal-version:       1.18
 synopsis:            Find, replace, and edit text patterns with Megaparsec parsers
 homepage:            https://github.com/jamesdbrock/replace-megaparsec
diff --git a/src/Replace/Megaparsec.hs b/src/Replace/Megaparsec.hs
--- a/src/Replace/Megaparsec.hs
+++ b/src/Replace/Megaparsec.hs
@@ -55,7 +55,6 @@
 import Data.Bifunctor
 import Data.Functor.Identity
 import Data.Proxy
-import Control.Exception (throw)
 import Data.Typeable
 import Control.Monad
 import qualified Data.ByteString as B
@@ -68,34 +67,57 @@
 -- == Separate and capture
 --
 -- Parser combinator to find all of the non-overlapping ocurrences
--- of the pattern @sep@ in a text stream. Separate the stream into sections:
+-- of the pattern parser @sep@ in a text stream.
+-- The 'sepCap' parser will always consume its entire input and can never fail.
 --
+-- === Output
+--
+-- The input stream is separated into a list of sections:
+--
 -- * sections which can parsed by the pattern @sep@ will be captured as
 --   matching sections in 'Right'
 -- * non-matching sections of the stream will be captured in 'Left'.
 --
--- This parser will always consume its entire input and can never fail.
--- If there are no pattern matches, then the entire input stream will be
--- returned as a non-matching 'Left' section.
+-- There are two constraints on the output:
 --
--- The pattern matching parser @sep@ will not be allowed to succeed without
--- consuming any input. If we allow the parser to match a zero-width pattern,
+-- * The output list will non-empty. If there are no pattern matches, then
+--   the entire input stream will be returned as one non-matching 'Left' section.
+--   If the input is @""@ then the output list will be @[Left ""]@.
+-- * The output list will not contain two consecutive 'Left's.
+--
+-- === Zero-width matches forbidden
+--
+-- If the pattern matching parser @sep@ would succeed without consuming any
+-- input then 'sepCap' will force it to fail.
+-- If we allow @sep@ to match a zero-width pattern,
 -- then it can match the same zero-width pattern again at the same position
 -- on the next iteration, which would result in an infinite number of
--- overlapping pattern matches. So, for example, the
--- pattern @many digitChar@, which can match zero occurences of a digit,
--- will be treated by @sepCap@ as @some digitChar@, and required to match
--- at least one digit.
+-- overlapping pattern matches.
 --
+-- === Special accelerated inputs
+--
+-- There are specialization re-write rules to speed up this function when
+-- the input type is "Data.Text" or "Data.Bytestring".
+--
+-- === Error parameter
+--
+-- The error type parameter @e@ for @sep@ should usually be 'Data.Void',
+-- because @sep@ fails on every token in a non-matching 'Left' section,
+-- so parser failures will not be reported.
+--
+-- === Notes
+--
 -- This @sepCap@ parser combinator is the basis for all of the other
--- features of this module. It is similar to the @sep*@ family of functions
+-- features of this module.
+--
+-- It is similar to the @sep*@ family of functions
 -- found in
 -- <http://hackage.haskell.org/package/parser-combinators/docs/Control-Monad-Combinators.html parser-combinators>
 -- and
 -- <http://hackage.haskell.org/package/parsers/docs/Text-Parser-Combinators.html parsers>
 -- but, importantly, it returns the parsed result of the @sep@ parser instead
--- of throwing it away.
---
+-- of throwing it away, like
+-- <http://hackage.haskell.org/package/parser-combinators/docs/Control-Monad-Combinators.html#v:manyTill_ manyTill_>.
 sepCap
     :: forall e s m a. (MonadParsec e s m)
     => m a -- ^ The pattern matching parser @sep@
@@ -233,12 +255,9 @@
 -- We need the @Monoid s@ instance so that we can 'Data.Monoid.mconcat' the output
 -- stream.
 --
--- We need @Typeable s@ and @Show s@ for 'Control.Exception.throw'. In theory
--- this function should never throw an exception, because it only throws
--- when the 'sepCap' parser fails, and the 'sepCap' parser
--- can never fail. The error type parameter @e@ should usually be 'Data.Void'.
+-- The error type parameter @e@ should usually be 'Data.Void'.
 streamEdit
-    :: forall e s a. (Show e, ShowErrorComponent e, Typeable e, Stream s, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s)
+    :: forall e s a. (Ord e, Stream s, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s)
     => Parsec e s a
         -- ^ The parser @sep@ for the pattern of interest.
     -> (a -> s)
@@ -264,7 +283,7 @@
 -- If you want the @editor@ function or the parser @sep@ to remember some state,
 -- then run this in a stateful monad.
 streamEditT
-    :: forall e s m a. (Show e, ShowErrorComponent e, Typeable e, Stream s, Monad m, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s)
+    :: forall e s m a. (Ord e, Stream s, Monad m, Monoid s, Tokens s ~ s, Show s, Show (Token s), Typeable s)
     => ParsecT e s m a
         -- ^ The parser @sep@ for the pattern of interest.
     -> (a -> m s)
@@ -275,9 +294,7 @@
     -> m s
 streamEditT sep editor input = do
     runParserT (sepCap sep) "" input >>= \case
-        (Left err) -> throw err
-        -- sepCap can never fail, but if it does, throw.
-        -- Don't use MonadFail because Identity is not a MonadFail.
+        (Left _) -> undefined -- sepCap can never fail
         (Right r) -> fmap mconcat $ traverse (either return editor) r
 {-# INLINABLE streamEditT #-}
 
