diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Revision history for bytesmith
 
+## 0.3.8.0 -- 2021-10-11
+
+* Add `peek` and `peek'` to `Data.Bytes.Parser.Latin`.
+* Add inline pragmas to most functions to prevent cost centers.
+* Add support for WordRep-to-LiftedRep in Rebindable module.
+* Allow building with newer Contiguous.
+* Export `uneffectful`.
+
 ## 0.3.7.0 -- 2020-07-27
 
 * Add `Data.Bytes.Parser.Base128` module for Base-128 encoding.
diff --git a/bytesmith.cabal b/bytesmith.cabal
--- a/bytesmith.cabal
+++ b/bytesmith.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.2
 name: bytesmith
-version: 0.3.7.0
+version: 0.3.8.0
 synopsis: Nonresumable byte parser
 description:
   Parse bytes as fast as possible. This is a nonresumable parser
@@ -35,7 +35,7 @@
     , base >=4.12 && <5
     , bytestring >=0.10.8 && <0.11
     , byteslice >=0.1.4 && <0.3
-    , contiguous >= 0.4 && < 0.6
+    , contiguous >= 0.4 && < 0.7
     , primitive >=0.7 && <0.8
     , text-short >=0.1.3 && <0.2
     , run-st >=0.1 && <0.2
diff --git a/src/Data/Bytes/Parser.hs b/src/Data/Bytes/Parser.hs
--- a/src/Data/Bytes/Parser.hs
+++ b/src/Data/Bytes/Parser.hs
@@ -100,7 +100,7 @@
 
 import Prelude hiding (length,any,fail,takeWhile,take,replicate)
 
-import Data.Bytes.Parser.Internal (InternalResult(..),Parser(..),ST#,unboxBytes)
+import Data.Bytes.Parser.Internal (Parser(..),ST#,unboxBytes)
 import Data.Bytes.Parser.Internal (boxBytes,Result#,uneffectful,fail)
 import Data.Bytes.Parser.Internal (uneffectful#,uneffectfulInt#)
 import Data.Bytes.Parser.Types (Result(Failure,Success),Slice(Slice))
@@ -114,6 +114,7 @@
 import Data.Primitive.Contiguous (Contiguous,Element)
 
 import qualified Data.Bytes as B
+import qualified Data.Bytes.Parser.Internal as Internal
 import qualified Data.Primitive as PM
 import qualified Data.Primitive.Contiguous as C
 import qualified GHC.Exts as Exts
@@ -121,6 +122,7 @@
 -- | Parse a byte sequence. This can succeed even if the
 -- entire slice was not consumed by the parser.
 parseBytes :: forall e a. (forall s. Parser e s a) -> Bytes -> Result e a
+{-# inline parseBytes #-}
 parseBytes p !b = runResultST action
   where
   action :: forall s. ST# s (Result# e a)
@@ -131,6 +133,7 @@
 -- Just like 'parseBytesEither', this does not impose any checks on the length
 -- of the remaining input.
 parseBytesMaybe :: forall e a. (forall s. Parser e s a) -> Bytes -> Maybe a
+{-# inline parseBytesMaybe #-}
 parseBytesMaybe p !b = runMaybeST action
   where
   action :: forall s. ST# s (Result# e a)
@@ -141,6 +144,7 @@
 -- remaining length. This does not, however, require the remaining
 -- length to be zero. Use 'endOfInput' to accomplish that.
 parseBytesEither :: forall e a. (forall s. Parser e s a) -> Bytes -> Either e a
+{-# inline parseBytesEither #-}
 parseBytesEither p !b = runEitherST action
   where
   action :: forall s. ST# s (Result# e a)
@@ -149,12 +153,14 @@
 
 -- Similar to runResultST
 runMaybeST :: (forall s. ST# s (Result# e x)) -> Maybe x
+{-# inline runMaybeST #-}
 runMaybeST f = case (runRW# (\s0 -> case f s0 of { (# _, r #) -> r })) of
   (# _ | #) -> Nothing
   (# | (# x, _, _ #) #) -> Just x
 
 -- Similar to runResultST
 runEitherST :: (forall s. ST# s (Result# e x)) -> Either e x
+{-# inline runEitherST #-}
 runEitherST f = case (runRW# (\s0 -> case f s0 of { (# _, r #) -> r })) of
   (# e | #) -> Left e
   (# | (# x, _, _ #) #) -> Right x
@@ -165,18 +171,21 @@
 -- it avoids the additional boxing that the Success data
 -- constructor would normally cause.
 runResultST :: (forall s. ST# s (Result# e x)) -> Result e x
+{-# inline runResultST #-}
 runResultST f = case (runRW# (\s0 -> case f s0 of { (# _, r #) -> r })) of
   (# e | #) -> Failure e
   (# | (# x, off, len #) #) -> Success (Slice (I# off) (I# len) x)
 
 -- | Variant of 'parseBytes' that accepts an unsliced 'ByteArray'.
 parseByteArray :: (forall s. Parser e s a) -> ByteArray -> Result e a
+{-# inline parseByteArray #-}
 parseByteArray p b =
   parseBytes p (Bytes b 0 (PM.sizeofByteArray b))
 
 -- | Variant of 'parseBytes' that allows the parser to be run
 -- as part of an existing effectful context.
 parseBytesEffectfully :: Parser e s a -> Bytes -> ST s (Result e a)
+{-# inline parseBytesEffectfully #-}
 parseBytesEffectfully (Parser f) !b = ST
   (\s0 -> case f (unboxBytes b) s0 of
     (# s1, r #) -> (# s1, boxPublicResult r #)
@@ -184,12 +193,14 @@
 
 -- | Lift an effectful computation into a parser.
 effect :: ST s a -> Parser e s a
+{-# inline effect #-}
 effect (ST f) = Parser
   ( \(# _, off, len #) s0 -> case f s0 of
     (# s1, a #) -> (# s1, (# | (# a, off, len #) #) #)
   )
 
 byteArray :: e -> ByteArray -> Parser e s ()
+{-# inline byteArray #-}
 byteArray e !expected = bytes e (B.fromByteArray expected)
 
 -- | Consume input matching the byte sequence.
@@ -235,8 +246,8 @@
 any e = uneffectful $ \chunk -> if length chunk > 0
   then
     let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8
-     in InternalSuccess w (offset chunk + 1) (length chunk - 1)
-  else InternalFailure e
+     in Internal.Success w (offset chunk + 1) (length chunk - 1)
+  else Internal.Failure e
 
 -- | Match any byte, to perform lookahead. Returns 'Nothing' if
 --   end of input has been reached. Does not consume any input.
@@ -246,18 +257,20 @@
 --   because such parsers loop until a failure occurs. Careless
 --   use will thus result in an infinite loop.
 peek :: Parser e s (Maybe Word8)
+{-# inline peek #-}
 peek = uneffectful $ \chunk ->
   let v = if length chunk > 0
         then Just (B.unsafeIndex chunk 0)
         else Nothing
-  in InternalSuccess v (offset chunk) (length chunk)
+  in Internal.Success v (offset chunk) (length chunk)
 
 -- | Match any byte, to perform lookahead. Does not consume any
 --   input, but will fail if end of input has been reached.
 peek' :: e -> Parser e s Word8
+{-# inline peek' #-}
 peek' e = uneffectful $ \chunk -> if length chunk > 0
-  then InternalSuccess (B.unsafeIndex chunk 0) (offset chunk) (length chunk)
-  else InternalFailure e
+  then Internal.Success (B.unsafeIndex chunk 0) (offset chunk) (length chunk)
+  else Internal.Failure e
 
 -- | A stateful scanner. The predicate consumes and transforms a
 --   state argument, and each transformed state is passed to
@@ -271,6 +284,7 @@
 --   combinators such a 'many', because such parsers loop until a
 --   failure occurs. Careless use will thus result in an infinite loop.
 scan :: state -> (state -> Word8 -> Maybe state) -> Parser e s state
+{-# inline scan #-}
 scan s0 t = do
   let go s = do
         mw <- peek
@@ -288,13 +302,14 @@
 {-# inline anyUnsafe #-}
 anyUnsafe = uneffectful $ \chunk ->
   let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8
-   in InternalSuccess w (offset chunk + 1) (length chunk - 1)
+   in Internal.Success w (offset chunk + 1) (length chunk - 1)
 
--- | Take while the predicate is matched. This is always inlined.
+-- | Take while the predicate is matched. This is always inlined. This
+-- always succeeds.
 takeWhile :: (Word8 -> Bool) -> Parser e s Bytes
 {-# inline takeWhile #-}
 takeWhile f = uneffectful $ \chunk -> case B.takeWhile f chunk of
-  bs -> InternalSuccess bs (offset chunk + length bs) (length chunk - length bs)
+  bs -> Internal.Success bs (offset chunk + length bs) (length chunk - length bs)
 
 -- | Take bytes until the specified byte is encountered. Consumes
 -- the matched byte as well. Fails if the byte is not present.
@@ -315,6 +330,7 @@
 -- | Skip all characters until the character from the is encountered
 -- and then consume the matching byte as well.
 skipTrailedBy :: e -> Word8 -> Parser e s ()
+{-# inline skipTrailedBy #-}
 skipTrailedBy e !w = uneffectful# (\c -> skipUntilConsumeByteLoop e w c)
 
 skipUntilConsumeByteLoop ::
@@ -337,6 +353,7 @@
   -> Word8 -- ^ First trailer, @False@ indicates that this was encountered
   -> Word8 -- ^ Second trailer, @True@ indicates that this was encountered
   -> Parser e s Bool
+{-# inline skipTrailedBy2 #-}
 skipTrailedBy2 e !wa !wb = boxBool (skipTrailedBy2# e wa wb)
 
 skipTrailedBy2# ::
@@ -344,6 +361,7 @@
   -> Word8 -- ^ First trailer, 0 indicates that this was encountered
   -> Word8 -- ^ Second trailer, 1 indicates that this was encountered
   -> Parser e s Int#
+{-# inline skipTrailedBy2# #-}
 skipTrailedBy2# e !wa !wb =
   uneffectfulInt# (\c -> skipUntilConsumeByteEitherLoop e wa wb c)
 
@@ -353,6 +371,7 @@
   -> Word8 -- ^ Second trailer, 1 indicates that this was encountered
   -> Word8 -- ^ Third trailer, 2 indicates that this was encountered
   -> Parser e s Int#
+{-# inline skipTrailedBy3# #-}
 skipTrailedBy3# e !wa !wb !wc =
   uneffectfulInt# (\c -> skipUntilConsumeByte3Loop e wa wb wc c)
 
@@ -390,20 +409,20 @@
 {-# inline take #-}
 take e n = uneffectful $ \chunk -> if n <= B.length chunk
   then case B.unsafeTake n chunk of
-    bs -> InternalSuccess bs (offset chunk + n) (length chunk - n)
-  else InternalFailure e
+    bs -> Internal.Success bs (offset chunk + n) (length chunk - n)
+  else Internal.Failure e
 
 -- | Consume all remaining bytes in the input.
 remaining :: Parser e s Bytes
 {-# inline remaining #-}
 remaining = uneffectful $ \chunk ->
-  InternalSuccess chunk (offset chunk + length chunk) 0
+  Internal.Success chunk (offset chunk + length chunk) 0
 
 -- | Return all remaining bytes in the input without consuming them.
 peekRemaining :: Parser e s Bytes
 {-# inline peekRemaining #-}
 peekRemaining = uneffectful $ \b@(Bytes _ off len) ->
-  InternalSuccess b off len
+  Internal.Success b off len
 
 -- | Skip while the predicate is matched. This is always inlined.
 skipWhile :: (Word8 -> Bool) -> Parser e s ()
@@ -428,35 +447,38 @@
 --   if the predicate @p@ returns 'True' on the transformed value.
 --   The parser returns the transformed byte that was parsed.
 satisfyWith :: e -> (Word8 -> a) -> (a -> Bool) -> Parser e s a
+{-# inline satisfyWith #-}
 satisfyWith e f p = uneffectful $ \chunk -> if length chunk > 1
   then case B.unsafeIndex chunk 1 of
     w ->
       let v = f w
       in if p v
-        then InternalSuccess v (offset chunk + 1) (length chunk - 1)
-        else InternalFailure e
-  else InternalFailure e
+        then Internal.Success v (offset chunk + 1) (length chunk - 1)
+        else Internal.Failure e
+  else Internal.Failure e
 
 -- | Fails if there is still more input remaining.
 endOfInput :: e -> Parser e s ()
--- GHC should decide to inline this after optimization.
+{-# inline endOfInput #-}
 endOfInput e = uneffectful $ \chunk -> if length chunk == 0
-  then InternalSuccess () (offset chunk) 0
-  else InternalFailure e
+  then Internal.Success () (offset chunk) 0
+  else Internal.Failure e
 
 -- | Returns true if there are no more bytes in the input. Returns
 -- false otherwise. Always succeeds.
 isEndOfInput :: Parser e s Bool
--- GHC should decide to inline this after optimization.
+{-# inline isEndOfInput #-}
 isEndOfInput = uneffectful $ \chunk ->
-  InternalSuccess (length chunk == 0) (offset chunk) (length chunk)
+  Internal.Success (length chunk == 0) (offset chunk) (length chunk)
 
 boxPublicResult :: Result# e a -> Result e a
+{-# inline boxPublicResult #-}
 boxPublicResult (# | (# a, b, c #) #) = Success (Slice (I# b) (I# c) a)
 boxPublicResult (# e | #) = Failure e
 
 -- | Convert a 'Word32' parser to a 'Word#' parser.
 unboxWord32 :: Parser e s Word32 -> Parser e s Word#
+{-# inline unboxWord32 #-}
 unboxWord32 (Parser f) = Parser
   (\x s0 -> case f x s0 of
     (# s1, r #) -> case r of
@@ -466,6 +488,7 @@
 
 -- | Convert a @(Int,Int)@ parser to a @(# Int#, Int# #)@ parser.
 unboxIntPair :: Parser e s (Int,Int) -> Parser e s (# Int#, Int# #)
+{-# inline unboxIntPair #-}
 unboxIntPair (Parser f) = Parser
   (\x s0 -> case f x s0 of
     (# s1, r #) -> case r of
@@ -476,6 +499,7 @@
 -- | Convert a 'Word#' parser to a 'Word32' parser. Precondition:
 -- the argument parser only returns words less than 4294967296.
 boxWord32 :: Parser e s Word# -> Parser e s Word32
+{-# inline boxWord32 #-}
 boxWord32 (Parser f) = Parser
   (\x s0 -> case f x s0 of
     (# s1, r #) -> case r of
@@ -505,6 +529,7 @@
 
 -- | Convert a @(# Int#, Int# #)@ parser to a @(Int,Int)@ parser.
 boxIntPair :: Parser e s (# Int#, Int# #) -> Parser e s (Int,Int)
+{-# inline boxIntPair #-}
 boxIntPair (Parser f) = Parser
   (\x s0 -> case f x s0 of
     (# s1, r #) -> case r of
@@ -664,6 +689,7 @@
   -> Int -- ^ Exact number of bytes delimited parser is expected to consume
   -> Parser e s a -- ^ Parser to execute in delimited context
   -> Parser e s a
+{-# inline delimit #-}
 delimit esz eleftovers (I# n) (Parser f) = Parser
   ( \(# arr, off, len #) s0 -> case len >=# n of
     1# -> case f (# arr, off, n #) s0 of
@@ -696,4 +722,5 @@
   go 0
 
 unI :: Int -> Int#
+{-# inline unI #-}
 unI (I# w) = w
diff --git a/src/Data/Bytes/Parser/Ascii.hs b/src/Data/Bytes/Parser/Ascii.hs
--- a/src/Data/Bytes/Parser/Ascii.hs
+++ b/src/Data/Bytes/Parser/Ascii.hs
@@ -57,7 +57,7 @@
 import Data.Bits (clearBit)
 import Data.Bytes.Types (Bytes(..))
 import Data.Bytes.Parser.Internal (Parser(..),uneffectful,Result#,uneffectful#)
-import Data.Bytes.Parser.Internal (InternalResult(..),indexLatinCharArray,upcastUnitSuccess)
+import Data.Bytes.Parser.Internal (Result(..),indexLatinCharArray,upcastUnitSuccess)
 import Data.Char (ord)
 import Data.Word (Word8)
 import Data.Text.Short (ShortText)
@@ -79,11 +79,12 @@
 -- Precondition: The argument must be a letter (@[a-zA-Z]@). Behavior is
 -- undefined if it is not.
 charInsensitive :: e -> Char -> Parser e s ()
+{-# inline charInsensitive #-}
 charInsensitive e !c = uneffectful $ \chunk -> if length chunk > 0
   then if clearBit (PM.indexByteArray (array chunk) (offset chunk) :: Word8) 5 == w
-    then InternalSuccess () (offset chunk + 1) (length chunk - 1)
-    else InternalFailure e
-  else InternalFailure e
+    then Success () (offset chunk + 1) (length chunk - 1)
+    else Failure e
+  else Failure e
   where
   w = clearBit (fromIntegral @Int @Word8 (ord c)) 5
 
@@ -91,6 +92,7 @@
 -- the trailer as well. This fails if the trailer is not
 -- found or if any non-ASCII characters are encountered.
 skipTrailedBy :: e -> Char -> Parser e s ()
+{-# inline skipTrailedBy #-}
 skipTrailedBy e !c = do
   let go = do
         !d <- any e
@@ -142,13 +144,14 @@
 
 -- | Consumes and returns the next character in the input.
 any :: e -> Parser e s Char
+{-# inline any #-}
 any e = uneffectful $ \chunk -> if length chunk > 0
   then
     let c = indexLatinCharArray (array chunk) (offset chunk)
      in if c < '\128'
-          then InternalSuccess c (offset chunk + 1) (length chunk - 1)
-          else InternalFailure e
-  else InternalFailure e
+          then Success c (offset chunk + 1) (length chunk - 1)
+          else Failure e
+  else Failure e
 
 -- | Variant of 'any' with unboxed result.
 any# :: e -> Parser e s Char#
@@ -164,22 +167,24 @@
   )
 
 unI :: Int -> Int#
+{-# inline unI #-}
 unI (I# w) = w
 
 -- | Examine the next byte without consuming it, interpret it as an
 -- ASCII-encoded character. This fails if the byte is above @0x7F@ or
 -- if the end of input has been reached.
 peek :: e -> Parser e s Char
+{-# inline peek #-}
 peek e = uneffectful $ \chunk -> if length chunk > 0
   then
     let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8
      in if w < 128
-          then InternalSuccess
+          then Success
                  (C# (chr# (unI (fromIntegral w))))
                  (offset chunk)
                  (length chunk)
-          else InternalFailure e
-  else InternalFailure e
+          else Failure e
+  else Failure e
 
 -- | Consume the next byte, interpreting it as an ASCII-encoded character.
 -- Fails if the byte is above @0x7F@. Returns @Nothing@ if the
@@ -190,12 +195,12 @@
   then
     let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8
      in if w < 128
-          then InternalSuccess
+          then Success
                  (Just (C# (chr# (unI (fromIntegral w)))))
                  (offset chunk + 1)
                  (length chunk - 1)
-          else InternalFailure e
-  else InternalSuccess Nothing (offset chunk) (length chunk)
+          else Failure e
+  else Success Nothing (offset chunk) (length chunk)
 
 -- | Consume characters matching the predicate. The stops when it
 -- encounters a non-matching character or when it encounters a byte
@@ -218,18 +223,21 @@
 -- | Skip uppercase and lowercase letters until a non-alpha
 -- character is encountered.
 skipAlpha :: Parser e s ()
+{-# inline skipAlpha #-}
 skipAlpha = uneffectful# $ \c ->
   upcastUnitSuccess (skipAlphaAsciiLoop c)
 
 -- | Skip uppercase and lowercase letters until a non-alpha
 -- character is encountered.
 skipAlpha1 :: e -> Parser e s ()
+{-# inline skipAlpha1 #-}
 skipAlpha1 e = uneffectful# $ \c ->
   skipAlphaAsciiLoop1Start e c
 
 skipAlphaAsciiLoop ::
      Bytes -- Chunk
   -> (# Int#, Int# #)
+{-# inline skipAlphaAsciiLoop #-}
 skipAlphaAsciiLoop !c = if length c > 0
   then
     let w = indexLatinCharArray (array c) (offset c)
@@ -242,6 +250,7 @@
      e
   -> Bytes -- chunk
   -> Result# e ()
+{-# inline skipAlphaAsciiLoop1Start #-}
 skipAlphaAsciiLoop1Start e !c = if length c > 0
   then 
     let w = indexLatinCharArray (array c) (offset c)
@@ -251,5 +260,5 @@
   else (# e | #)
 
 byteArrayToShortByteString :: PM.ByteArray -> BSS.ShortByteString
+{-# inline byteArrayToShortByteString #-}
 byteArrayToShortByteString (PM.ByteArray x) = BSS.SBS x
-
diff --git a/src/Data/Bytes/Parser/BigEndian.hs b/src/Data/Bytes/Parser/BigEndian.hs
--- a/src/Data/Bytes/Parser/BigEndian.hs
+++ b/src/Data/Bytes/Parser/BigEndian.hs
@@ -45,7 +45,7 @@
 import Data.Bits ((.|.),unsafeShiftL)
 import Data.Bytes.Types (Bytes(..))
 import Data.Bytes.Parser.Internal (Parser,uneffectful)
-import Data.Bytes.Parser.Internal (InternalResult(..))
+import Data.Bytes.Parser.Internal (Result(..))
 import Data.Bytes.Parser.Internal (swapArray16,swapArray32,swapArray64,swapArray256)
 import Data.Bytes.Parser.Internal (swapArray128)
 import Data.Word (Word8,Word16,Word32,Word64)
@@ -146,10 +146,10 @@
   then
     let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8
         wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8
-     in InternalSuccess
+     in Success
           (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wa) 8 .|. fromIntegral wb))
           (offset chunk + 2) (length chunk - 2)
-  else InternalFailure e
+  else Failure e
 
 -- | Unsigned 32-bit word.
 word32 :: e -> Parser e s Word32
@@ -159,7 +159,7 @@
         wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8
         wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8
         wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8
-     in InternalSuccess
+     in Success
           (fromIntegral @Word @Word32
             ( unsafeShiftL (fromIntegral wa) 24 .|.
               unsafeShiftL (fromIntegral wb) 16 .|.
@@ -168,7 +168,7 @@
             )
           )
           (offset chunk + 4) (length chunk - 4)
-  else InternalFailure e
+  else Failure e
 
 -- | Unsigned 64-bit word.
 word64 :: e -> Parser e s Word64
@@ -182,7 +182,7 @@
         wf = PM.indexByteArray (array chunk) (offset chunk + 5) :: Word8
         wg = PM.indexByteArray (array chunk) (offset chunk + 6) :: Word8
         wh = PM.indexByteArray (array chunk) (offset chunk + 7) :: Word8
-     in InternalSuccess
+     in Success
           ( unsafeShiftL (fromIntegral wa) 56 .|.
             unsafeShiftL (fromIntegral wb) 48 .|.
             unsafeShiftL (fromIntegral wc) 40 .|.
@@ -193,7 +193,7 @@
             fromIntegral wh
           )
           (offset chunk + 8) (length chunk - 8)
-  else InternalFailure e
+  else Failure e
 
 -- | Unsigned 128-bit word.
 word128 :: e -> Parser e s Word128
diff --git a/src/Data/Bytes/Parser/Internal.hs b/src/Data/Bytes/Parser/Internal.hs
--- a/src/Data/Bytes/Parser/Internal.hs
+++ b/src/Data/Bytes/Parser/Internal.hs
@@ -19,7 +19,7 @@
 
 module Data.Bytes.Parser.Internal
   ( Parser(..)
-  , InternalResult(..)
+  , Result(..)
   , InternalStep(..)
   , Bytes#
   , ST#
@@ -63,16 +63,16 @@
     { runParser :: (# ByteArray#, Int#, Int# #) -> ST# s (Result# e a) } -> Parser e s a
 
 -- The result of running a parser. Used internally.
-data InternalResult e a
-  = InternalFailure e
+data Result e a
+  = Failure e
     -- An error message indicating what went wrong.
-  | InternalSuccess !a !Int !Int
+  | Success !a !Int !Int
     -- The parsed value, the offset after the last consumed byte, and the
     -- number of bytes remaining in parsed slice.
 
 data InternalStep a = InternalStep !a !Int !Int
 
-uneffectful :: (Bytes -> InternalResult e a) -> Parser e s a
+uneffectful :: (Bytes -> Result e a) -> Parser e s a
 {-# inline uneffectful #-}
 uneffectful f = Parser
   ( \b s0 -> (# s0, unboxResult (f (boxBytes b)) #) )
@@ -98,9 +98,10 @@
   (# e
   | (# a, Int#, Int# #) #) -- ints are offset and length
 
-unboxResult :: InternalResult e a -> Result# e a
-unboxResult (InternalSuccess a (I# b) (I# c)) = (# | (# a, b, c #) #)
-unboxResult (InternalFailure e) = (# e | #)
+unboxResult :: Result e a -> Result# e a
+{-# inline unboxResult #-}
+unboxResult (Success a (I# b) (I# c)) = (# | (# a, b, c #) #)
+unboxResult (Failure e) = (# e | #)
 
 -- | Combines the error messages using '<>' when both
 -- parsers fail.
@@ -122,7 +123,8 @@
 fail ::
      e -- ^ Error message
   -> Parser e s a
-fail e = uneffectful $ \_ -> InternalFailure e
+{-# inline fail #-}
+fail e = uneffectful $ \_ -> Failure e
 
 instance Applicative (Parser e s) where
   pure = pureParser
diff --git a/src/Data/Bytes/Parser/Latin.hs b/src/Data/Bytes/Parser/Latin.hs
--- a/src/Data/Bytes/Parser/Latin.hs
+++ b/src/Data/Bytes/Parser/Latin.hs
@@ -43,6 +43,9 @@
   , opt#
     -- * Many Characters
   , takeTrailedBy
+    -- * Lookahead
+  , peek
+  , peek'
     -- * Skip
   , skipDigits
   , skipDigits1
@@ -94,7 +97,7 @@
 import Data.Bytes.Types (Bytes(..))
 import Data.Bytes.Parser.Internal (InternalStep(..),unfailing)
 import Data.Bytes.Parser.Internal (Parser(..),ST#,uneffectful,Result#,uneffectful#)
-import Data.Bytes.Parser.Internal (InternalResult(..),indexLatinCharArray,upcastUnitSuccess)
+import Data.Bytes.Parser.Internal (Result(..),indexLatinCharArray,upcastUnitSuccess)
 import Data.Bytes.Parser.Internal (boxBytes)
 import Data.Bytes.Parser (bindFromLiftedToInt,isEndOfInput,endOfInput)
 import Data.Bytes.Parser.Unsafe (expose,cursor,unconsume)
@@ -116,10 +119,10 @@
 -- of the input has been reached. This never fails.
 trySatisfy :: (Char -> Bool) -> Parser e s Bool
 trySatisfy f = uneffectful $ \chunk -> case length chunk of
-  0 -> InternalSuccess False (offset chunk) (length chunk)
+  0 -> Success False (offset chunk) (length chunk)
   _ -> case f (indexLatinCharArray (array chunk) (offset chunk)) of
-    True -> InternalSuccess True (offset chunk + 1) (length chunk - 1)
-    False -> InternalSuccess False (offset chunk) (length chunk)
+    True -> Success True (offset chunk + 1) (length chunk - 1)
+    False -> Success False (offset chunk) (length chunk)
 
 -- | Runs the function on the next character in the input. If the
 -- function returns @Just@, this consumes the character and then
@@ -144,58 +147,59 @@
 -- | Consume the next character, failing if it does not
 -- match the expected value or if there is no more input.
 char :: e -> Char -> Parser e s ()
--- GHC should decide to inline this after optimization.
+{-# inline char #-}
 char e !c = uneffectful $ \chunk -> if length chunk > 0
   then if indexLatinCharArray (array chunk) (offset chunk) == c
-    then InternalSuccess () (offset chunk + 1) (length chunk - 1)
-    else InternalFailure e
-  else InternalFailure e
+    then Success () (offset chunk + 1) (length chunk - 1)
+    else Failure e
+  else Failure e
 
 -- | Consume the next two characters, failing if they do
 -- not match the expected values.
 --
 -- > char2 e a b === char e a *> char e b
 char2 :: e -> Char -> Char -> Parser e s ()
--- GHC should decide to inline this after optimization.
+{-# inline char2 #-}
 char2 e !c0 !c1 = uneffectful $ \chunk ->
   if | length chunk > 1
      , indexLatinCharArray (array chunk) (offset chunk) == c0
      , indexLatinCharArray (array chunk) (offset chunk + 1) == c1
-         -> InternalSuccess () (offset chunk + 2) (length chunk - 2)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 2) (length chunk - 2)
+     | otherwise -> Failure e
 
 -- | Consume three characters, failing if they do
 -- not match the expected values.
 --
 -- > char3 e a b c === char e a *> char e b *> char e c
 char3 :: e -> Char -> Char -> Char -> Parser e s ()
--- GHC should decide to inline this after optimization.
+{-# inline char3 #-}
 char3 e !c0 !c1 !c2 = uneffectful $ \chunk ->
   if | length chunk > 2
      , indexLatinCharArray (array chunk) (offset chunk) == c0
      , indexLatinCharArray (array chunk) (offset chunk + 1) == c1
      , indexLatinCharArray (array chunk) (offset chunk + 2) == c2
-         -> InternalSuccess () (offset chunk + 3) (length chunk - 3)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 3) (length chunk - 3)
+     | otherwise -> Failure e
 
 -- | Consume four characters, failing if they do
 -- not match the expected values.
 --
 -- > char4 e a b c d === char e a *> char e b *> char e c *> char e d
 char4 :: e -> Char -> Char -> Char -> Char -> Parser e s ()
--- GHC should decide to inline this after optimization.
+{-# inline char4 #-}
 char4 e !c0 !c1 !c2 !c3 = uneffectful $ \chunk ->
   if | length chunk > 3
      , indexLatinCharArray (array chunk) (offset chunk) == c0
      , indexLatinCharArray (array chunk) (offset chunk + 1) == c1
      , indexLatinCharArray (array chunk) (offset chunk + 2) == c2
      , indexLatinCharArray (array chunk) (offset chunk + 3) == c3
-         -> InternalSuccess () (offset chunk + 4) (length chunk - 4)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 4) (length chunk - 4)
+     | otherwise -> Failure e
 
 -- | Consume five characters, failing if they do
 -- not match the expected values.
 char5 :: e -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char5 #-}
 char5 e !c0 !c1 !c2 !c3 !c4 = uneffectful $ \chunk ->
   if | length chunk > 4
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -203,12 +207,13 @@
      , indexLatinCharArray (array chunk) (offset chunk + 2) == c2
      , indexLatinCharArray (array chunk) (offset chunk + 3) == c3
      , indexLatinCharArray (array chunk) (offset chunk + 4) == c4
-         -> InternalSuccess () (offset chunk + 5) (length chunk - 5)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 5) (length chunk - 5)
+     | otherwise -> Failure e
 
 -- | Consume six characters, failing if they do
 -- not match the expected values.
 char6 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char6 #-}
 char6 e !c0 !c1 !c2 !c3 !c4 !c5 = uneffectful $ \chunk ->
   if | length chunk > 5
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -217,12 +222,13 @@
      , indexLatinCharArray (array chunk) (offset chunk + 3) == c3
      , indexLatinCharArray (array chunk) (offset chunk + 4) == c4
      , indexLatinCharArray (array chunk) (offset chunk + 5) == c5
-         -> InternalSuccess () (offset chunk + 6) (length chunk - 6)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 6) (length chunk - 6)
+     | otherwise -> Failure e
 
 -- | Consume seven characters, failing if they do
 -- not match the expected values.
 char7 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char7 #-}
 char7 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 = uneffectful $ \chunk ->
   if | length chunk > 6
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -232,12 +238,13 @@
      , indexLatinCharArray (array chunk) (offset chunk + 4) == c4
      , indexLatinCharArray (array chunk) (offset chunk + 5) == c5
      , indexLatinCharArray (array chunk) (offset chunk + 6) == c6
-         -> InternalSuccess () (offset chunk + 7) (length chunk - 7)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 7) (length chunk - 7)
+     | otherwise -> Failure e
 
 -- | Consume eight characters, failing if they do
 -- not match the expected values.
 char8 :: e -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char8 #-}
 char8 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 = uneffectful $ \chunk ->
   if | length chunk > 7
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -248,13 +255,14 @@
      , indexLatinCharArray (array chunk) (offset chunk + 5) == c5
      , indexLatinCharArray (array chunk) (offset chunk + 6) == c6
      , indexLatinCharArray (array chunk) (offset chunk + 7) == c7
-         -> InternalSuccess () (offset chunk + 8) (length chunk - 8)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 8) (length chunk - 8)
+     | otherwise -> Failure e
 
 -- | Consume nine characters, failing if they do
 -- not match the expected values.
 char9 :: e -> Char -> Char -> Char -> Char
   -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char9 #-}
 char9 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 = uneffectful $ \chunk ->
   if | length chunk > 8
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -266,13 +274,14 @@
      , indexLatinCharArray (array chunk) (offset chunk + 6) == c6
      , indexLatinCharArray (array chunk) (offset chunk + 7) == c7
      , indexLatinCharArray (array chunk) (offset chunk + 8) == c8
-         -> InternalSuccess () (offset chunk + 9) (length chunk - 9)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 9) (length chunk - 9)
+     | otherwise -> Failure e
 
 -- | Consume ten characters, failing if they do
 -- not match the expected values.
 char10 :: e -> Char -> Char -> Char -> Char -> Char
   -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char10 #-}
 char10 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 = uneffectful $ \chunk ->
   if | length chunk > 9
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -285,13 +294,14 @@
      , indexLatinCharArray (array chunk) (offset chunk + 7) == c7
      , indexLatinCharArray (array chunk) (offset chunk + 8) == c8
      , indexLatinCharArray (array chunk) (offset chunk + 9) == c9
-         -> InternalSuccess () (offset chunk + 10) (length chunk - 10)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 10) (length chunk - 10)
+     | otherwise -> Failure e
 
 -- | Consume eleven characters, failing if they do
 -- not match the expected values.
 char11 :: e -> Char -> Char -> Char -> Char -> Char -> Char
   -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char11 #-}
 char11 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 = uneffectful $ \chunk ->
   if | length chunk > 10
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -305,13 +315,14 @@
      , indexLatinCharArray (array chunk) (offset chunk + 8) == c8
      , indexLatinCharArray (array chunk) (offset chunk + 9) == c9
      , indexLatinCharArray (array chunk) (offset chunk + 10) == c10
-         -> InternalSuccess () (offset chunk + 11) (length chunk - 11)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 11) (length chunk - 11)
+     | otherwise -> Failure e
 
 -- | Consume twelve characters, failing if they do
 -- not match the expected values.
 char12 :: e -> Char -> Char -> Char -> Char -> Char -> Char
   -> Char -> Char -> Char -> Char -> Char -> Char -> Parser e s ()
+{-# inline char12 #-}
 char12 e !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 = uneffectful $ \chunk ->
   if | length chunk > 11
      , indexLatinCharArray (array chunk) (offset chunk) == c0
@@ -326,24 +337,26 @@
      , indexLatinCharArray (array chunk) (offset chunk + 9) == c9
      , indexLatinCharArray (array chunk) (offset chunk + 10) == c10
      , indexLatinCharArray (array chunk) (offset chunk + 11) == c11
-         -> InternalSuccess () (offset chunk + 12) (length chunk - 12)
-     | otherwise -> InternalFailure e
+         -> Success () (offset chunk + 12) (length chunk - 12)
+     | otherwise -> Failure e
 
 -- | Consumes and returns the next character in the input.
 any :: e -> Parser e s Char
+{-# inline any #-}
 any e = uneffectful $ \chunk -> if length chunk > 0
   then
     let c = indexLatinCharArray (array chunk) (offset chunk)
-     in InternalSuccess c (offset chunk + 1) (length chunk - 1)
-  else InternalFailure e
+     in Success c (offset chunk + 1) (length chunk - 1)
+  else Failure e
 
 -- | Consume a character from the input or return @Nothing@ if
 -- end of the stream has been reached. Since ISO 8859-1 maps every
 -- bytes to a character, this parser never fails.
 opt :: Parser e s (Maybe Char)
+{-# inline opt #-}
 opt = uneffectful $ \chunk -> case length chunk of
-  0 -> InternalSuccess Nothing (offset chunk) (length chunk)
-  _ -> InternalSuccess
+  0 -> Success Nothing (offset chunk) (length chunk)
+  _ -> Success
     (Just (indexLatinCharArray (array chunk) (offset chunk)))
     (offset chunk + 1) (length chunk - 1)
 
@@ -382,6 +395,7 @@
 -- | Variant of 'skipDigits' that requires at least one digit
 -- to be present.
 skipDigits1 :: e -> Parser e s ()
+{-# inline skipDigits1 #-}
 skipDigits1 e = uneffectful# $ \c ->
   skipDigitsAscii1LoopStart e c
 
@@ -397,12 +411,14 @@
 -- | Skip the character any number of times. This succeeds
 -- even if the character was not present.
 skipChar :: Char -> Parser e s ()
+{-# inline skipChar #-}
 skipChar !w = uneffectful# $ \c ->
   upcastUnitSuccess (skipLoop w c)
 
 -- | Skip the character any number of times. It must occur
 -- at least once or else this will fail.
 skipChar1 :: e -> Char -> Parser e s ()
+{-# inline skipChar1 #-}
 skipChar1 e !w = uneffectful# $ \c ->
   skipLoop1Start e w c
 
@@ -539,11 +555,13 @@
   !len@(I# len# ) = length chunk0
 
 upcastWordResult :: Result# e Word# -> Result# e Word
+{-# inline upcastWordResult #-}
 upcastWordResult (# e | #) = (# e | #)
 upcastWordResult (# | (# a, b, c #) #) = (# | (# W# a, b, c #) #)
 
 -- This only works on 64-bit platforms.
 upcastWord64Result :: Result# e Word# -> Result# e Word64
+{-# inline upcastWord64Result #-}
 upcastWord64Result (# e | #) = (# e | #)
 upcastWord64Result (# | (# a, b, c #) #) = (# | (# W64# a, b, c #) #)
 
@@ -599,16 +617,19 @@
 
 -- Precondition: the word is small enough
 upcastWord16Result :: Result# e Word# -> Result# e Word16
+{-# inline upcastWord16Result #-}
 upcastWord16Result (# e | #) = (# e | #)
 upcastWord16Result (# | (# a, b, c #) #) = (# | (# W16# a, b, c #) #)
 
 -- Precondition: the word is small enough
 upcastWord32Result :: Result# e Word# -> Result# e Word32
+{-# inline upcastWord32Result #-}
 upcastWord32Result (# e | #) = (# e | #)
 upcastWord32Result (# | (# a, b, c #) #) = (# | (# W32# a, b, c #) #)
 
 -- Precondition: the word is small enough
 upcastWord8Result :: Result# e Word# -> Result# e Word8
+{-# inline upcastWord8Result #-}
 upcastWord8Result (# e | #) = (# e | #)
 upcastWord8Result (# | (# a, b, c #) #) = (# | (# W8# a, b, c #) #)
 
@@ -1076,25 +1097,25 @@
 -- @[a-f0-9]@.
 hexNibbleLower :: e -> Parser e s Word
 hexNibbleLower e = uneffectful $ \chunk -> case length chunk of
-  0 -> InternalFailure e
+  0 -> Failure e
   _ ->
     let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in
-    if | w >= 48 && w < 58 -> InternalSuccess (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)
-       | w >= 97 && w < 103 -> InternalSuccess (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)
-       | otherwise -> InternalFailure e
+    if | w >= 48 && w < 58 -> Success (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)
+       | w >= 97 && w < 103 -> Success (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)
+       | otherwise -> Failure e
 
 -- | Consume a single character that is the case-insensitive hexadecimal
 -- encoding of a 4-bit word. Fails if the character is not in the class
 -- @[a-fA-F0-9]@.
 hexNibble :: e -> Parser e s Word
 hexNibble e = uneffectful $ \chunk -> case length chunk of
-  0 -> InternalFailure e
+  0 -> Failure e
   _ ->
     let w = PM.indexByteArray (array chunk) (offset chunk) :: Word8 in
-    if | w >= 48 && w < 58 -> InternalSuccess (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)
-       | w >= 65 && w < 71 -> InternalSuccess (fromIntegral w - 55) (offset chunk + 1) (length chunk - 1)
-       | w >= 97 && w < 103 -> InternalSuccess (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)
-       | otherwise -> InternalFailure e
+    if | w >= 48 && w < 58 -> Success (fromIntegral w - 48) (offset chunk + 1) (length chunk - 1)
+       | w >= 65 && w < 71 -> Success (fromIntegral w - 55) (offset chunk + 1) (length chunk - 1)
+       | w >= 97 && w < 103 -> Success (fromIntegral w - 87) (offset chunk + 1) (length chunk - 1)
+       | otherwise -> Failure e
 
 -- | Consume a single character that is the lowercase hexadecimal
 -- encoding of a 4-bit word. Returns @Nothing@ without consuming
@@ -1126,6 +1147,7 @@
 -- Returns the maximum machine word if the argument is not
 -- the ASCII encoding of a hexadecimal digit.
 oneHex :: Word8 -> Word
+{-# inline oneHex #-}
 oneHex w
   | w >= 48 && w < 58 = (fromIntegral w - 48)
   | w >= 65 && w < 71 = (fromIntegral w - 55)
@@ -1141,6 +1163,7 @@
   | otherwise = Nothing
 
 uneffectfulWord# :: (Bytes -> Result# e Word#) -> Parser e s Word#
+{-# inline uneffectfulWord# #-}
 uneffectfulWord# f = Parser
   ( \b s0 -> (# s0, (f (boxBytes b)) #) )
 
@@ -1149,6 +1172,7 @@
 -- Postcondition: when overflow is false, the resulting
 -- word is less than or equal to the upper bound
 positivePushBase10 :: Word -> Word -> Word -> (Bool,Word)
+{-# inline positivePushBase10 #-}
 positivePushBase10 (W# a) (W# b) (W# upper) = 
   let !(# ca, r0 #) = Exts.timesWord2# a 10##
       !r1 = Exts.plusWord# r0 b
@@ -1158,6 +1182,7 @@
    in (case c of { 0## -> False; _ -> True }, W# r1)
 
 unsignedPushBase10 :: Word -> Word -> (Bool,Word)
+{-# inline unsignedPushBase10 #-}
 unsignedPushBase10 (W# a) (W# b) = 
   let !(# ca, r0 #) = Exts.timesWord2# a 10##
       !r1 = Exts.plusWord# r0 b
@@ -1184,8 +1209,32 @@
 {-# inline anyUnsafe #-}
 anyUnsafe = uneffectful $ \chunk ->
   let w = indexCharArray (array chunk) (offset chunk) :: Char
-   in InternalSuccess w (offset chunk + 1) (length chunk - 1)
+   in Success w (offset chunk + 1) (length chunk - 1)
 
 -- Reads one byte and interprets it as Latin1-encoded character.
 indexCharArray :: PM.ByteArray -> Int -> Char
+{-# inline indexCharArray #-}
 indexCharArray (PM.ByteArray x) (I# i) = C# (indexCharArray# x i)
+
+-- | Match any character, to perform lookahead. Returns 'Nothing' if
+--   end of input has been reached. Does not consume any input.
+--
+--   /Note/: Because this parser does not fail, do not use it
+--   with combinators such as 'many', because such as 'many',
+--   because such parsers loop until a failure occurs. Careless
+--   use will thus result in an infinite loop.
+peek :: Parser e s (Maybe Char)
+{-# inline peek #-}
+peek = uneffectful $ \(Bytes arr off len) ->
+  let v = if len > 0
+        then Just (indexCharArray arr off)
+        else Nothing
+  in Success v off len
+
+-- | Match any byte, to perform lookahead. Does not consume any
+--   input, but will fail if end of input has been reached.
+peek' :: e -> Parser e s Char
+{-# inline peek' #-}
+peek' e = uneffectful $ \(Bytes arr off len) -> if len > 0
+  then Success (indexCharArray arr off) off len
+  else Failure e
diff --git a/src/Data/Bytes/Parser/LittleEndian.hs b/src/Data/Bytes/Parser/LittleEndian.hs
--- a/src/Data/Bytes/Parser/LittleEndian.hs
+++ b/src/Data/Bytes/Parser/LittleEndian.hs
@@ -49,7 +49,7 @@
 import Data.Primitive (ByteArray(..),PrimArray(..))
 import Data.Bytes.Types (Bytes(..))
 import Data.Bytes.Parser.Internal (Parser,uneffectful)
-import Data.Bytes.Parser.Internal (InternalResult(..))
+import Data.Bytes.Parser.Internal (Result(..))
 import Data.Bytes.Parser.Internal (swapArray16,swapArray32)
 import Data.Bytes.Parser.Internal (swapArray64,swapArray128,swapArray256)
 import Data.Word (Word8,Word16,Word32,Word64)
@@ -158,10 +158,10 @@
   then
     let wa = PM.indexByteArray (array chunk) (offset chunk) :: Word8
         wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8
-     in InternalSuccess
+     in Success
           (fromIntegral @Word @Word16 (unsafeShiftL (fromIntegral wb) 8 .|. fromIntegral wa))
           (offset chunk + 2) (length chunk - 2)
-  else InternalFailure e
+  else Failure e
 
 -- | Unsigned 32-bit word.
 word32 :: e -> Parser e s Word32
@@ -171,7 +171,7 @@
         wb = PM.indexByteArray (array chunk) (offset chunk + 1) :: Word8
         wc = PM.indexByteArray (array chunk) (offset chunk + 2) :: Word8
         wd = PM.indexByteArray (array chunk) (offset chunk + 3) :: Word8
-     in InternalSuccess
+     in Success
           (fromIntegral @Word @Word32
             ( unsafeShiftL (fromIntegral wd) 24 .|.
               unsafeShiftL (fromIntegral wc) 16 .|.
@@ -180,7 +180,7 @@
             )
           )
           (offset chunk + 4) (length chunk - 4)
-  else InternalFailure e
+  else Failure e
 
 -- | Unsigned 64-bit word.
 word64 :: e -> Parser e s Word64
@@ -194,7 +194,7 @@
         wf = PM.indexByteArray (array chunk) (offset chunk + 5) :: Word8
         wg = PM.indexByteArray (array chunk) (offset chunk + 6) :: Word8
         wh = PM.indexByteArray (array chunk) (offset chunk + 7) :: Word8
-     in InternalSuccess
+     in Success
           ( unsafeShiftL (fromIntegral wh) 56 .|.
             unsafeShiftL (fromIntegral wg) 48 .|.
             unsafeShiftL (fromIntegral wf) 40 .|.
@@ -205,7 +205,7 @@
             fromIntegral wa
           )
           (offset chunk + 8) (length chunk - 8)
-  else InternalFailure e
+  else Failure e
 
 -- | Unsigned 256-bit word.
 word256 :: e -> Parser e s Word256
diff --git a/src/Data/Bytes/Parser/Rebindable.hs b/src/Data/Bytes/Parser/Rebindable.hs
--- a/src/Data/Bytes/Parser/Rebindable.hs
+++ b/src/Data/Bytes/Parser/Rebindable.hs
@@ -75,6 +75,17 @@
         runParser (g y) (# arr, b, c #) s1
   )
 
+bindWordParser :: forall (a :: TYPE 'WordRep) e s b.
+  Parser e s a -> (a -> Parser e s b) -> Parser e s b
+{-# inline bindWordParser #-}
+bindWordParser (Parser f) g = Parser
+  (\x@(# arr, _, _ #) s0 -> case f x s0 of
+    (# s1, r0 #) -> case r0 of
+      (# e | #) -> (# s1, (# e | #) #)
+      (# | (# y, b, c #) #) ->
+        runParser (g y) (# arr, b, c #) s1
+  )
+
 sequenceIntParser :: forall (a :: TYPE 'IntRep) e s b.
   Parser e s a -> Parser e s b -> Parser e s b
 {-# inline sequenceIntParser #-}
@@ -85,6 +96,16 @@
       (# | (# _, b, c #) #) -> g (# arr, b, c #) s1
   )
 
+sequenceWordParser :: forall (a :: TYPE 'WordRep) e s b.
+  Parser e s a -> Parser e s b -> Parser e s b
+{-# inline sequenceWordParser #-}
+sequenceWordParser (Parser f) (Parser g) = Parser
+  (\x@(# arr, _, _ #) s0 -> case f x s0 of
+    (# s1, r0 #) -> case r0 of
+      (# e | #) -> (# s1, (# e | #) #)
+      (# | (# _, b, c #) #) -> g (# arr, b, c #) s1
+  )
+
 pureIntPairParser :: forall (a :: TYPE ('TupleRep '[ 'IntRep, 'IntRep])) e s.
   a -> Parser e s a
 {-# inline pureIntPairParser #-}
@@ -171,6 +192,12 @@
   {-# inline (>>) #-}
   (>>=) = bindParser
   (>>) = sequenceParser
+
+instance Bind 'WordRep 'LiftedRep where
+  {-# inline (>>=) #-}
+  {-# inline (>>) #-}
+  (>>=) = bindWordParser
+  (>>) = sequenceWordParser
 
 instance Bind 'IntRep 'LiftedRep where
   {-# inline (>>=) #-}
diff --git a/src/Data/Bytes/Parser/Unsafe.hs b/src/Data/Bytes/Parser/Unsafe.hs
--- a/src/Data/Bytes/Parser/Unsafe.hs
+++ b/src/Data/Bytes/Parser/Unsafe.hs
@@ -29,23 +29,24 @@
   , expose
   , unconsume
   , jump
+  , uneffectful
   ) where
 
 import Prelude hiding (length)
 
-import Data.Primitive (ByteArray)
-import Data.Bytes.Types (Bytes(..))
 import Data.Bytes.Parser.Internal (Parser(..),uneffectful,uneffectfulInt#)
-import Data.Bytes.Parser.Internal (InternalResult(..))
-
+import Data.Bytes.Parser.Internal (Result(..))
+import Data.Bytes.Types (Bytes(..))
+import Data.Primitive (ByteArray)
 import GHC.Exts (Int#,Int(I#))
 
+
 -- | Get the current offset into the chunk. Using this makes
 -- it possible to observe the internal difference between 'Bytes'
 -- that refer to equivalent slices. Be careful.
 cursor :: Parser e s Int
 cursor = uneffectful $ \Bytes{offset,length} ->
-  InternalSuccess offset offset length
+  Success offset offset length
 
 -- | Variant of 'cursor' with unboxed result.
 cursor# :: Parser e s Int#
@@ -57,18 +58,17 @@
 -- use this is you know what you're doing.
 expose :: Parser e s ByteArray
 expose = uneffectful $ \Bytes{length,offset,array} ->
-  InternalSuccess array offset length
+  Success array offset length
 
 -- | Move the cursor back by @n@ bytes. Precondition: you
 -- must have previously consumed at least @n@ bytes.
 unconsume :: Int -> Parser e s ()
 unconsume n = uneffectful $ \Bytes{length,offset} ->
-  InternalSuccess () (offset - n) (length + n)
+  Success () (offset - n) (length + n)
 
 -- | Set the position to the given index. Precondition: the index
 -- must be valid. It should be the result of an earlier call to
 -- 'cursor'.
 jump :: Int -> Parser e s ()
 jump ix = uneffectful $ \(Bytes{length,offset}) ->
-  InternalSuccess () ix (length + (offset - ix))
-
+  Success () ix (length + (offset - ix))
