diff --git a/Data/Attoparsec.hs b/Data/Attoparsec.hs
--- a/Data/Attoparsec.hs
+++ b/Data/Attoparsec.hs
@@ -56,6 +56,7 @@
     , I.string
     , I.skipWhile
     , I.take
+    , I.scan
     , I.takeWhile
     , I.takeWhile1
     , I.takeTill
diff --git a/Data/Attoparsec/Char8.hs b/Data/Attoparsec/Char8.hs
--- a/Data/Attoparsec/Char8.hs
+++ b/Data/Attoparsec/Char8.hs
@@ -64,9 +64,10 @@
     , skipSpace
     , skipWhile
     , I.take
-    , takeTill
+    , scan
     , takeWhile
     , takeWhile1
+    , takeTill
 
     -- * Text parsing
     , I.endOfLine
@@ -273,6 +274,21 @@
 takeWhile p = I.takeWhile (p . w2c)
 {-# INLINE takeWhile #-}
 
+-- | A stateful scanner.  The predicate consumes and transforms a
+-- state argument, and each transformed state is passed to successive
+-- invocations of the predicate on each byte of the input until one
+-- returns 'Nothing' or the input ends.
+--
+-- This parser does not fail.  It will return an empty string if the
+-- predicate returns 'Nothing' on the first byte of input.
+--
+-- /Note/: Because this parser does not fail, do not use it with
+-- combinators such as 'many', because such parsers loop until a
+-- failure occurs.  Careless use will thus result in an infinite loop.
+scan :: s -> (s -> Char -> Maybe s) -> Parser B.ByteString
+scan s0 p = I.scan s0 (\s -> p s . w2c)
+{-# INLINE scan #-}
+
 -- | Consume input as long as the predicate returns 'False'
 -- (i.e. until it returns 'True'), and return the consumed input.
 --
@@ -293,7 +309,7 @@
 
 -- | Skip over white space.
 skipSpace :: Parser ()
-skipSpace = skipWhile isSpace >> return ()
+skipSpace = I.skipWhile isSpace_w8
 {-# INLINE skipSpace #-}
 
 -- | A predicate that matches either a carriage return @\'\\r\'@ or
diff --git a/Data/Attoparsec/Internal.hs b/Data/Attoparsec/Internal.hs
--- a/Data/Attoparsec/Internal.hs
+++ b/Data/Attoparsec/Internal.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE Rank2Types, RecordWildCards #-}
+{-# LANGUAGE BangPatterns, Rank2Types, OverloadedStrings, RecordWildCards #-}
 -- |
 -- Module      :  Data.Attoparsec.Internal
 -- Copyright   :  Bryan O'Sullivan 2007-2010
@@ -46,6 +46,7 @@
     , string
     , stringTransform
     , take
+    , scan
     , takeWhile
     , takeWhile1
     , takeTill
@@ -66,8 +67,9 @@
 import Data.Word (Word8)
 import Foreign.ForeignPtr (withForeignPtr)
 import Foreign.Ptr (castPtr, plusPtr)
-import Foreign.Storable (Storable(peek, sizeOf))
+import Foreign.Storable (Storable(peek, sizeOf), peekByteOff)
 import Prelude hiding (getChar, take, takeWhile)
+import System.IO.Unsafe (unsafePerformIO)
 import qualified Data.ByteString as B8
 import qualified Data.ByteString.Char8 as B
 import qualified Data.ByteString.Internal as B
@@ -104,7 +106,7 @@
 
 data S = S {
       input  :: !B.ByteString
-    , _added :: !B.ByteString
+    , _added :: B.ByteString
     , more  :: !More
     } deriving (Show)
 
@@ -148,6 +150,7 @@
 
 instance MonadPlus Parser where
     mzero = failDesc "mzero"
+    {-# INLINE mzero #-}
     mplus = plus
 
 fmapP :: (a -> b) -> Parser a -> Parser b
@@ -174,8 +177,14 @@
     (*>)   = (>>)
     x <* y = x >>= \a -> y >> return a
 
+instance Monoid (Parser a) where
+    mempty  = failDesc "mempty"
+    {-# INLINE mempty #-}
+    mappend = plus
+
 instance Alternative Parser where
     empty = failDesc "empty"
+    {-# INLINE empty #-}
     (<|>) = plus
 
 failDesc :: String -> Parser a
@@ -255,7 +264,7 @@
 -- | The parser @skip p@ succeeds for any byte for which the predicate
 -- @p@ returns 'True'.
 --
--- >digit = satisfy isDigit
+-- >skipDigit = skip isDigit
 -- >    where isDigit w = w >= 48 && w <= 57
 skip :: (Word8 -> Bool) -> Parser ()
 skip p = do
@@ -335,6 +344,7 @@
       t <- B8.dropWhile p <$> get
       put t
       when (B.null t) go
+{-# INLINE skipWhile #-}
 
 -- | Consume input as long as the predicate returns 'False'
 -- (i.e. until it returns 'True'), and return the consumed input.
@@ -359,19 +369,55 @@
 -- combinators such as 'many', because such parsers loop until a
 -- failure occurs.  Careless use will thus result in an infinite loop.
 takeWhile :: (Word8 -> Bool) -> Parser B.ByteString
-takeWhile p = go
+takeWhile p = (B.concat . reverse) `fmap` go []
  where
-  go = do
+  go acc = do
     input <- wantInput
     if input
       then do
         (h,t) <- B8.span p <$> get
         put t
         if B.null t
-          then (h+++) `fmapP` go
-          else return h
-      else return B.empty
+          then go (h:acc)
+          else return (h:acc)
+      else return acc
 
+-- | A stateful scanner.  The predicate consumes and transforms a
+-- state argument, and each transformed state is passed to successive
+-- invocations of the predicate on each byte of the input until one
+-- returns 'Nothing' or the input ends.
+--
+-- This parser does not fail.  It will return an empty string if the
+-- predicate returns 'Nothing' on the first byte of input.
+--
+-- /Note/: Because this parser does not fail, do not use it with
+-- combinators such as 'many', because such parsers loop until a
+-- failure occurs.  Careless use will thus result in an infinite loop.
+scan :: s -> (s -> Word8 -> Maybe s) -> Parser B.ByteString
+scan s0 p = (B.concat . reverse) `fmap` go [] s0
+ where
+  go acc s1 = do
+    input <- wantInput
+    if input
+      then do
+        let scanner (B.PS fp off len) =
+              withForeignPtr fp $ \ptr -> do
+                let inner !i !s | i == off+len = return (i-off,s)
+                                | otherwise = do
+                                            w <- peekByteOff ptr i
+                                            case p s w of
+                                              Just s' -> inner (i+1) s'
+                                              Nothing -> return (i-off,s)
+                (i,s') <- inner off s1
+                return (B.PS fp off i, B.PS fp (off+i) (len-i),s')
+        (h,t,s') <- (unsafePerformIO . scanner) <$> get
+        put t
+        if B.null t
+          then go (h:acc) s'
+          else return (h:acc)
+      else return acc
+{-# INLINE scan #-}    
+
 -- | Consume input as long as the predicate returns 'True', and return
 -- the consumed input.
 --
@@ -437,7 +483,7 @@
 -- | Match either a single newline character @\'\\n\'@, or a carriage
 -- return followed by a newline character @\"\\r\\n\"@.
 endOfLine :: Parser ()
-endOfLine = (word8 10 >> return ()) <|> (string (B.pack "\r\n") >> return ())
+endOfLine = (word8 10 >> return ()) <|> (string "\r\n" >> return ())
 
 --- | Name the parser, in case failure occurs.
 (<?>) :: Parser a
diff --git a/attoparsec.cabal b/attoparsec.cabal
--- a/attoparsec.cabal
+++ b/attoparsec.cabal
@@ -1,5 +1,5 @@
 name:            attoparsec
-version:         0.8.3.0
+version:         0.8.4.0
 license:         BSD3
 license-file:    LICENSE
 category:        Text, Parsing
@@ -31,6 +31,10 @@
     examples/TestRFC2616.hs
     examples/rfc2616.c
 
+Flag developer
+  Description: Whether to build the library in development mode
+  Default: False
+
 flag split-base
 flag applicative-in-base
 
@@ -56,7 +60,9 @@
                    Data.Attoparsec.Lazy
   other-modules:   Data.Attoparsec.Internal
   ghc-options:     -Wall
-  ghc-prof-options: -auto-all
+
+  if flag(developer)
+    ghc-prof-options: -auto-all
 
 source-repository head
   type:     mercurial
