diff --git a/Data/Csv.hs b/Data/Csv.hs
--- a/Data/Csv.hs
+++ b/Data/Csv.hs
@@ -17,6 +17,9 @@
     -- * Treating CSV data as opaque byte strings
     -- $generic-processing
 
+    -- * Custom type conversions
+    -- $customtypeconversions
+
     -- * Encoding and decoding
     -- $encoding
       decode
@@ -50,6 +53,8 @@
     -- $indexbased
     , FromRecord(..)
     , Parser
+    , runParser
+    , index
     , (.!)
     , ToRecord(..)
     , record
@@ -58,16 +63,21 @@
     -- ** Name-based record conversion
     -- $namebased
     , FromNamedRecord(..)
+    , lookup
     , (.:)
     , ToNamedRecord(..)
     , namedRecord
+    , namedField
     , (.=)
 
     -- ** Field conversion
+    -- $fieldconversion
     , FromField(..)
     , ToField(..)
     ) where
 
+import Prelude hiding (lookup)
+
 import Data.Csv.Conversion
 import Data.Csv.Encoding
 import Data.Csv.Types
@@ -76,9 +86,8 @@
 --
 -- A short encoding usage example:
 --
--- @ >>> 'encode' $ fromList [(\"John\" :: Text, 27), (\"Jane\", 28)]
--- Chunk \"John,27\\r\\nJane,28\\r\\n\" Empty
--- @
+-- > >>> encode $ fromList [("John" :: Text, 27), ("Jane", 28)]
+-- > Chunk "John,27\r\nJane,28\r\n" Empty
 --
 -- Since string literals are overloaded we have to supply a type
 -- signature as the compiler couldn't deduce which string type (i.e.
@@ -88,9 +97,8 @@
 --
 -- A short decoding usage example:
 --
--- @ >>> 'decode' 'False' \"John,27\\r\\nJane,28\\r\\n\" :: Either String (Vector (Text, Int))
--- Right (fromList [(\"John\",27),(\"Jane\",28)])
--- @
+-- > >>> decode False "John,27\r\nJane,28\r\n" :: Either String (Vector (Text, Int))
+-- > Right (fromList [("John",27),("Jane",28)])
 --
 -- We pass 'False' as the first argument to indicate that the CSV
 -- input data isn't preceded by a header.
@@ -106,13 +114,37 @@
 -- parse a CSV file to a generic representation, just convert each
 -- record to a @'Vector' 'ByteString'@ value, like so:
 --
--- @ 'decode' 'False' \"John,27\\r\\nJane,28\\r\\n\" :: Either String (Vector (Vector ByteString))
--- Right (fromList [fromList [\"John\",\"27\"],fromList [\"Jane\",\"28\"]])
--- @
+-- > decode False "John,27\r\nJane,28\r\n" :: Either String (Vector (Vector ByteString))
+-- > Right (fromList [fromList ["John","27"],fromList ["Jane","28"]])
 --
 -- As the example output above shows, all the fields are returned as
 -- uninterpreted 'ByteString' values.
 
+-- $customtypeconversions
+--
+-- Most of the time the existing 'FromField' and 'ToField' instances
+-- do what you want. However, if you need to parse a different format
+-- (e.g. hex) but use a type (e.g. 'Int') for which there's already a
+-- 'FromField' instance, you need to use a @newtype@. Example:
+--
+-- > newtype Hex = Hex Int
+-- >
+-- > parseHex :: ByteString -> Parser Int
+-- > parseHex = ...
+-- >
+-- > instance FromField Hex where
+-- >     parseField s = Hex <$> parseHex s
+--
+-- Other than giving an explicit type signature, you can pattern match
+-- on the @newtype@ constructor to indicate which type conversion you
+-- want to have the library use:
+--
+-- > case decode False "0xff,0xaa\r\n0x11,0x22\r\n" of
+-- >     Left err -> putStrLn err
+-- >     Right v  -> forM_ v $ \ (Hex val1, Hex val2) ->
+-- >         print (val1, val2)
+
+
 -- $encoding
 --
 -- Encoding and decoding is a two step process. To encode a value, it
@@ -149,3 +181,10 @@
 -- These functions can be used to control how data is encoded and
 -- decoded. For example, they can be used to encode data in a
 -- tab-separated format instead of in a comma-separated format.
+
+-- $fieldconversion
+--
+-- The 'FromField' and 'ToField' classes define how to convert between
+-- 'Field's and values you care about (e.g. 'Int's). Most of the time
+-- you don't need to write your own instances as the standard ones
+-- cover most use cases.
diff --git a/Data/Csv/Conversion.hs b/Data/Csv/Conversion.hs
--- a/Data/Csv/Conversion.hs
+++ b/Data/Csv/Conversion.hs
@@ -16,14 +16,15 @@
     , ToField(..)
 
     -- * Parser
-    , Result(..)
     , Parser
-    , parse
-    , parseEither
+    , runParser
 
     -- * Accessors
+    , index
     , (.!)
+    , lookup
     , (.:)
+    , namedField
     , (.=)
     , record
     , namedRecord
@@ -31,7 +32,8 @@
 
 import Control.Applicative
 import Control.Monad
-import Data.Attoparsec.Char8 (double, number, parseOnly)
+import Data.Attoparsec.Char8 (double, parseOnly)
+import qualified Data.Attoparsec.Char8 as A8
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
 import qualified Data.ByteString.Lazy as L
@@ -49,7 +51,7 @@
 import qualified Data.Vector.Unboxed as U
 import Data.Word
 import GHC.Float (double2Float)
-import Prelude hiding (takeWhile)
+import Prelude hiding (lookup, takeWhile)
 
 import Data.Csv.Conversion.Internal
 import Data.Csv.Types
@@ -79,15 +81,14 @@
 --
 -- here's an example type and instance:
 --
--- @data Person = Person { name :: Text, age :: Int }
---
--- instance FromRecord Person where
---     parseRecord v
---         | 'V.length' v == 2 = Person '<$>'
---                           v '.!' 0 '<*>'
---                           v '.!' 1
---         | otherwise     = mzero
--- @
+-- > data Person = Person { name :: Text, age :: Int }
+-- >
+-- > instance FromRecord Person where
+-- >     parseRecord v
+-- >         | length v == 2 = Person <$>
+-- >                           v .! 0 <*>
+-- >                           v .! 1
+-- >         | otherwise     = mzero
 class FromRecord a where
     parseRecord :: Record -> Parser a
   
@@ -107,12 +108,11 @@
 --
 -- An example type and instance:
 --
--- @data Person = Person { name :: Text, age :: Int }
---
--- instance ToRecord Person where
---     toRecord (Person name age) = 'record' [
---        'toField' name, 'toField' age]
--- @
+-- > data Person = Person { name :: Text, age :: Int }
+-- >
+-- > instance ToRecord Person where
+-- >     toRecord (Person name age) = 'record' [
+-- >         'toField' name, 'toField' age]
 --
 -- Outputs data on this form:
 --
@@ -283,15 +283,14 @@
 --
 -- here's an example type and instance:
 --
--- @{-\# LANGUAGE OverloadedStrings \#-}
---
--- data Person = Person { name :: Text, age :: Int }
---
--- instance FromRecord Person where
---     parseNamedRecord m = Person '<$>'
---                          m '.:' \"name\" '<*>'
---                          m '.:' \"age\"
--- @
+-- > {-# LANGUAGE OverloadedStrings #-}
+-- >
+-- > data Person = Person { name :: Text, age :: Int }
+-- >
+-- > instance FromRecord Person where
+-- >     parseNamedRecord m = Person <$>
+-- >                          m .: "name" <*>
+-- >                          m .: "age"
 --
 -- Note the use of the @OverloadedStrings@ language extension which
 -- enables 'B8.ByteString' values to be written as string literals.
@@ -307,12 +306,11 @@
 --
 -- An example type and instance:
 --
--- @data Person = Person { name :: Text, age :: Int }
---
--- instance ToRecord Person where
---     toNamedRecord (Person name age) = 'namedRecord' [
---        \"name\" '.=' name, \"age\" '.=' age]
--- @
+-- > data Person = Person { name :: Text, age :: Int }
+-- >
+-- > instance ToRecord Person where
+-- >     toNamedRecord (Person name age) = namedRecord [
+-- >         "name" .= name, "age" .= age]
 class ToNamedRecord a where
     toNamedRecord :: a -> NamedRecord
 
@@ -347,17 +345,16 @@
 --
 -- Example type and instance:
 --
--- @{-\# LANGUAGE OverloadedStrings \#-}
---
--- data Color = Red | Green | Blue
---
--- instance FromField Color where
---     parseField s
---         | s == \"R\"  = pure Red
---         | s == \"G\"  = pure Green
---         | s == \"B\"  = pure Blue
---         | otherwise = mzero
--- @
+-- > {-# LANGUAGE OverloadedStrings #-}
+-- >
+-- > data Color = Red | Green | Blue
+-- >
+-- > instance FromField Color where
+-- >     parseField s
+-- >         | s == "R"  = pure Red
+-- >         | s == "G"  = pure Green
+-- >         | s == "B"  = pure Blue
+-- >         | otherwise = mzero
 class FromField a where
     parseField :: Field -> Parser a
 
@@ -365,30 +362,30 @@
 --
 -- Example type and instance:
 --
--- @{-\# LANGUAGE OverloadedStrings \#-}
---
--- data Color = Red | Green | Blue
---
--- instance ToField Color where
---     toField Red   = \"R\"
---     toField Green = \"G\"
---     toField Blue  = \"B\"
--- @
+-- > {-# LANGUAGE OverloadedStrings #-}
+-- >
+-- > data Color = Red | Green | Blue
+-- >
+-- > instance ToField Color where
+-- >     toField Red   = "R"
+-- >     toField Green = "G"
+-- >     toField Blue  = "B"
 class ToField a where
     toField :: a -> Field
 
--- | 'Nothing' if the field is empty, 'Just' otherwise.
+-- | 'Nothing' if the 'Field' is 'B.empty', 'Just' otherwise.
 instance FromField a => FromField (Maybe a) where
     parseField s
         | B.null s  = pure Nothing
         | otherwise = Just <$> parseField s
     {-# INLINE parseField #-}
 
--- | 'Nothing' is encoded as an empty field.
+-- | 'Nothing' is encoded as an 'B.empty' field.
 instance ToField a => ToField (Maybe a) where
     toField = maybe B.empty toField
     {-# INLINE toField #-}
 
+-- | Assumes UTF-8 encoding.
 instance FromField Char where
     parseField s
         | T.compareLength t 1 == EQ = pure (T.head t)
@@ -396,22 +393,29 @@
       where t = T.decodeUtf8 s
     {-# INLINE parseField #-}
 
+-- | Uses UTF-8 encoding.
 instance ToField Char where
     toField = toField . T.encodeUtf8 . T.singleton
     {-# INLINE toField #-}
 
+-- | Accepts same syntax as 'rational'.
 instance FromField Double where
     parseField = parseDouble
     {-# INLINE parseField #-}
 
+-- | Uses decimal notation or scientific notation, depending on the
+-- number.
 instance ToField Double where
     toField = realFloat
     {-# INLINE toField #-}
 
+-- | Accepts same syntax as 'rational'.
 instance FromField Float where
     parseField s = double2Float <$> parseDouble s
     {-# INLINE parseField #-}
 
+-- | Uses decimal notation or scientific notation, depending on the
+-- number.
 instance ToField Float where
     toField = realFloat
     {-# INLINE toField #-}
@@ -422,90 +426,112 @@
     Right n  -> pure n
 {-# INLINE parseDouble #-}
 
+-- | Accepts a signed decimal number.
 instance FromField Int where
-    parseField = parseIntegral "Int"
+    parseField = parseSigned "Int"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding with optional sign.
 instance ToField Int where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts a signed decimal number.
 instance FromField Integer where
-    parseField = parseIntegral "Integer"
+    parseField = parseSigned "Integer"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding with optional sign.
 instance ToField Integer where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts a signed decimal number.
 instance FromField Int8 where
-    parseField = parseIntegral "Int8"
+    parseField = parseSigned "Int8"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding with optional sign.
 instance ToField Int8 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts a signed decimal number.
 instance FromField Int16 where
-    parseField = parseIntegral "Int16"
+    parseField = parseSigned "Int16"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding with optional sign.
 instance ToField Int16 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts a signed decimal number.
 instance FromField Int32 where
-    parseField = parseIntegral "Int32"
+    parseField = parseSigned "Int32"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding with optional sign.
 instance ToField Int32 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts a signed decimal number.
 instance FromField Int64 where
-    parseField = parseIntegral "Int64"
+    parseField = parseSigned "Int64"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding with optional sign.
 instance ToField Int64 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts an unsigned decimal number.
 instance FromField Word where
-    parseField = parseIntegral "Word"
+    parseField = parseUnsigned "Word"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding.
 instance ToField Word where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts an unsigned decimal number.
 instance FromField Word8 where
-    parseField = parseIntegral "Word8"
+    parseField = parseUnsigned "Word8"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding.
 instance ToField Word8 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts an unsigned decimal number.
 instance FromField Word16 where
-    parseField = parseIntegral "Word16"
+    parseField = parseUnsigned "Word16"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding.
 instance ToField Word16 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts an unsigned decimal number.
 instance FromField Word32 where
-    parseField = parseIntegral "Word32"
+    parseField = parseUnsigned "Word32"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding.
 instance ToField Word32 where
     toField = decimal
     {-# INLINE toField #-}
 
+-- | Accepts an unsigned decimal number.
 instance FromField Word64 where
-    parseField = parseIntegral "Word64"
+    parseField = parseUnsigned "Word64"
     {-# INLINE parseField #-}
 
+-- | Uses decimal encoding.
 instance ToField Word64 where
     toField = decimal
     {-# INLINE toField #-}
@@ -556,12 +582,18 @@
     toField = toField . T.pack
     {-# INLINE toField #-}
 
-parseIntegral :: Integral a => String -> B.ByteString -> Parser a
-parseIntegral typ s = case parseOnly number s of
+parseSigned :: (Integral a, Num a) => String -> B.ByteString -> Parser a
+parseSigned typ s = case parseOnly (A8.signed A8.decimal) s of
     Left err -> typeError typ s (Just err)
-    Right n  -> pure (floor n)
-{-# INLINE parseIntegral #-}
+    Right n  -> pure n
+{-# INLINE parseSigned #-}
 
+parseUnsigned :: Integral a => String -> B.ByteString -> Parser a
+parseUnsigned typ s = case parseOnly A8.decimal s of
+    Left err -> typeError typ s (Just err)
+    Right n  -> pure n
+{-# INLINE parseUnsigned #-}
+
 typeError :: String -> B.ByteString -> Maybe String -> Parser a
 typeError typ s mmsg =
     fail $ "expected " ++ typ ++ ", got " ++ show (B8.unpack s) ++ cause
@@ -576,22 +608,42 @@
 -- | Retrieve the /n/th field in the given record. The result is
 -- 'empty' if the value cannot be converted to the desired type.
 -- Raises an exception if the index is out of bounds.
+--
+-- 'index' is a simple convenience function that is equivalent to
+-- @'parseField' (v '!' idx)@. If you're certain that the index is not
+-- out of bounds, using @'parseField' (`V.unsafeIndex` v idx)@ is
+-- somewhat faster.
+index :: FromField a => Record -> Int -> Parser a
+index v idx = parseField (v ! idx)
+{-# INLINE index #-}
+
+-- | Alias for 'index'.
 (.!) :: FromField a => Record -> Int -> Parser a
-v .! idx = parseField (v ! idx)
+(.!) = index
 {-# INLINE (.!) #-}
 
 -- | Retrieve a field in the given record by name.  The result is
 -- 'empty' if the field is missing or if the value cannot be converted
 -- to the desired type.
-(.:) :: FromField a => NamedRecord -> B.ByteString -> Parser a
-m .: name = maybe (fail err) parseField $ HM.lookup name m
+lookup :: FromField a => NamedRecord -> B.ByteString -> Parser a
+lookup m name = maybe (fail err) parseField $ HM.lookup name m
   where err = "no field named " ++ show (B8.unpack name)
+{-# INLINE lookup #-}
+
+-- | Alias for 'lookup'.
+(.:) :: FromField a => NamedRecord -> B.ByteString -> Parser a
+(.:) = lookup
 {-# INLINE (.:) #-}
 
 -- | Construct a pair from a name and a value.  For use with
 -- 'namedRecord'.
+namedField :: ToField a => B.ByteString -> a -> (B.ByteString, B.ByteString)
+namedField name val = (name, toField val)
+{-# INLINE namedField #-}
+
+-- | Alias for 'namedField'.
 (.=) :: ToField a => B.ByteString -> a -> (B.ByteString, B.ByteString)
-name .= val = (name, toField val)
+(.=) = namedField
 {-# INLINE (.=) #-}
 
 -- | Construct a record from a list of 'B.ByteString's.  Use 'toField'
@@ -607,48 +659,6 @@
 ------------------------------------------------------------------------
 -- Parser for converting records to data types
 
--- | The result of running a 'Parser'.
-data Result a = Error String
-              | Success a
-                deriving (Eq, Show)
-
-instance Functor Result where
-    fmap f (Success a) = Success (f a)
-    fmap _ (Error err) = Error err
-    {-# INLINE fmap #-}
-
-instance Monad Result where
-    return = Success
-    {-# INLINE return #-}
-    Success a >>= k = k a
-    Error err >>= _ = Error err
-    {-# INLINE (>>=) #-}
-
-instance Applicative Result where
-    pure  = return
-    {-# INLINE pure #-}
-    (<*>) = ap
-    {-# INLINE (<*>) #-}
-
-instance MonadPlus Result where
-    mzero = fail "mzero"
-    {-# INLINE mzero #-}
-    mplus a@(Success _) _ = a
-    mplus _ b             = b
-    {-# INLINE mplus #-}
-
-instance Alternative Result where
-    empty = mzero
-    {-# INLINE empty #-}
-    (<|>) = mplus
-    {-# INLINE (<|>) #-}
-
-instance Monoid (Result a) where
-    mempty  = fail "mempty"
-    {-# INLINE mempty #-}
-    mappend = mplus
-    {-# INLINE mappend #-}
-
 -- | Failure continuation.
 type Failure f r   = String -> f r
 -- | Success continuation.
@@ -659,15 +669,15 @@
 -- lets you compose several field conversions together in such a way
 -- that if any of them fail, the whole record conversion fails.
 newtype Parser a = Parser {
-      runParser :: forall f r.
-                   Failure f r
-                -> Success a f r
-                -> f r
+      unParser :: forall f r.
+                  Failure f r
+               -> Success a f r
+               -> f r
     }
 
 instance Monad Parser where
-    m >>= g = Parser $ \kf ks -> let ks' a = runParser (g a) kf ks
-                                 in runParser m kf ks'
+    m >>= g = Parser $ \kf ks -> let ks' a = unParser (g a) kf ks
+                                 in unParser m kf ks'
     {-# INLINE (>>=) #-}
     return a = Parser $ \_kf ks -> ks a
     {-# INLINE return #-}
@@ -676,7 +686,7 @@
 
 instance Functor Parser where
     fmap f m = Parser $ \kf ks -> let ks' a = ks (f a)
-                                  in runParser m kf ks'
+                                  in unParser m kf ks'
     {-# INLINE fmap #-}
 
 instance Applicative Parser where
@@ -694,8 +704,8 @@
 instance MonadPlus Parser where
     mzero = fail "mzero"
     {-# INLINE mzero #-}
-    mplus a b = Parser $ \kf ks -> let kf' _ = runParser b kf ks
-                                   in runParser a kf' ks
+    mplus a b = Parser $ \kf ks -> let kf' _ = unParser b kf ks
+                                   in unParser a kf' ks
     {-# INLINE mplus #-}
 
 instance Monoid (Parser a) where
@@ -711,24 +721,18 @@
   return (b a)
 {-# INLINE apP #-}
 
--- | Run a 'Parser'. Forces the value in the 'Success' or 'Error'
--- constructors to weak head normal form.
-parse :: Parser a -> Result a
-parse p = runParser p err success
-  where
-    err !errMsg = Error errMsg
-    success !x = Success x
-{-# INLINE parse #-}
-
--- | Run a 'Parser', returning either @'Left' msg@ or @'Right'
+-- | Run a 'Parser', returning either @'Left' errMsg@ or @'Right'
 -- result@. Forces the value in the 'Left' or 'Right' constructors to
 -- weak head normal form.
-parseEither :: Parser a -> Either String a
-parseEither p = runParser p left right
+--
+-- You most likely won't need to use this function directly, but it's
+-- included for completeness.
+runParser :: Parser a -> Either String a
+runParser p = unParser p left right
   where
     left !errMsg = Left errMsg
     right !x = Right x
-{-# INLINE parseEither #-}
+{-# INLINE runParser #-}
 
 #ifdef GENERICS
 
diff --git a/Data/Csv/Encoding.hs b/Data/Csv/Encoding.hs
--- a/Data/Csv/Encoding.hs
+++ b/Data/Csv/Encoding.hs
@@ -75,12 +75,12 @@
 decodeByName = decodeByNameWith defaultDecodeOptions
 {-# INLINE decodeByName #-}
 
--- | Efficiently serialize CVS records as a lazy 'L.ByteString'.
+-- | Efficiently serialize CSV records as a lazy 'L.ByteString'.
 encode :: ToRecord a => V.Vector a -> L.ByteString
 encode = encodeWith defaultEncodeOptions
 {-# INLINE encode #-}
 
--- | Efficiently serialize CVS records as a lazy 'L.ByteString'. The
+-- | Efficiently serialize CSV records as a lazy 'L.ByteString'. The
 -- header is written before any records and dictates the field order.
 encodeByName :: ToNamedRecord a => Header -> V.Vector a -> L.ByteString
 encodeByName = encodeByNameWith defaultEncodeOptions
@@ -96,7 +96,7 @@
                              -- skipped
            -> L.ByteString   -- ^ CSV data
            -> Either String (Vector a)
-decodeWith = decodeWithC (parse . parseCsv)
+decodeWith = decodeWithC (runParser . parseCsv)
 {-# INLINE [1] decodeWith #-}
 
 parseCsv :: FromRecord a => Csv -> Parser (Vector a)
@@ -122,7 +122,7 @@
              -> Either String (Vector (Vector B.ByteString))
 idDecodeWith = decodeWithC pure
 
-decodeWithC :: (Csv -> Result a) -> DecodeOptions -> Bool -> L.ByteString
+decodeWithC :: (Csv -> Either String a) -> DecodeOptions -> Bool -> L.ByteString
             -> Either String a
 decodeWithC convert !opts skipHeader = decodeWithP parser convert
   where parser
@@ -138,7 +138,7 @@
                  -> Either String (Header, Vector a)
 decodeByNameWith !opts =
     decodeWithP (csvWithHeader opts)
-    (\ (hdr, vs) -> (,) <$> pure hdr <*> (parse $ parseNamedCsv vs))
+    (\ (hdr, vs) -> (,) <$> pure hdr <*> (runParser $ parseNamedCsv vs))
 
 parseNamedCsv :: FromNamedRecord a => Vector NamedRecord -> Parser (Vector a)
 parseNamedCsv xs = V.fromList <$!> mapM' parseNamedRecord (V.toList xs)
@@ -146,10 +146,18 @@
 -- | Options that controls how data is encoded. These options can be
 -- used to e.g. encode data in a tab-separated format instead of in a
 -- comma-separated format.
+--
+-- To avoid having your program stop compiling when new fields are
+-- added to 'EncodeOptions', create option records by overriding
+-- values in 'defaultEncodeOptions'. Example:
+--
+-- > myOptions = defaultEncodeOptions {
+-- >       encDelimiter = fromIntegral (ord '\t')
+-- >     }
 data EncodeOptions = EncodeOptions
     { -- | Field delimiter.
       encDelimiter  :: {-# UNPACK #-} !Word8
-    }
+    } deriving (Eq, Show)
 
 -- | Encoding options for CSV files.
 defaultEncodeOptions :: EncodeOptions
@@ -228,12 +236,12 @@
 prependToAll _   []     = []
 prependToAll sep (x:xs) = sep <> x : prependToAll sep xs
 
-decodeWithP :: AL.Parser a -> (a -> Result b) -> L.ByteString -> Either String b
+decodeWithP :: AL.Parser a -> (a -> Either String b) -> L.ByteString -> Either String b
 decodeWithP p to s =
     case AL.parse p s of
       AL.Done _ v     -> case to v of
-          Success a -> Right a
-          Error msg -> Left $ "conversion error: " ++ msg
+          Right a  -> Right a
+          Left msg -> Left $ "conversion error: " ++ msg
       AL.Fail left _ msg -> Left $ "parse error (" ++ msg ++ ") at " ++
                             show (BL8.unpack left)
 {-# INLINE decodeWithP #-}
diff --git a/Data/Csv/Incremental.hs b/Data/Csv/Incremental.hs
--- a/Data/Csv/Incremental.hs
+++ b/Data/Csv/Incremental.hs
@@ -42,7 +42,7 @@
 import qualified Data.HashMap.Strict as HM
 import qualified Data.Vector as V
 
-import Data.Csv.Conversion hiding (Parser, Result, record, toNamedRecord)
+import Data.Csv.Conversion hiding (Parser, record, toNamedRecord)
 import qualified Data.Csv.Conversion as Conversion
 import Data.Csv.Parser
 import Data.Csv.Types
@@ -124,7 +124,8 @@
 feedEndOfInputH p            = p
 
 -- | Parse a CSV header in an incremental fashion. When done, the
--- 'HeaderParser' returns any unconsumed input in its second field.
+-- 'HeaderParser' returns any unconsumed input in the second field of
+-- the 'DoneH' constructor.
 decodeHeader :: HeaderParser B.ByteString
 decodeHeader = decodeHeaderWith defaultDecodeOptions
 
@@ -251,11 +252,11 @@
                  => DecodeOptions  -- ^ Decoding options
                  -> HeaderParser (Parser a)
 decodeByNameWith !opts =
-    PartialH (runParser . (decodeHeaderWith opts `feedChunkH`))
+    PartialH (go . (decodeHeaderWith opts `feedChunkH`))
   where
-    runParser (FailH rest msg) = FailH rest msg
-    runParser (PartialH k)     = PartialH $ \ s -> runParser (k s)
-    runParser (DoneH hdr rest) =
+    go (FailH rest msg) = FailH rest msg
+    go (PartialH k)     = PartialH $ \ s -> go (k s)
+    go (DoneH hdr rest) =
         DoneH hdr (decodeWithP (parseNamedRecord . toNamedRecord hdr) opts rest)
 
 -- Copied from Data.Csv.Parser
@@ -293,7 +294,7 @@
                  | otherwise   = convert r : acc
 
     parser = A.parse (record (decDelimiter opts) <* (endOfLine <|> endOfInput))
-    convert = parseEither . p
+    convert = runParser . p
 {-# INLINE decodeWithP #-}
 
 blankLine :: V.Vector B.ByteString -> Bool
diff --git a/Data/Csv/Parser.hs b/Data/Csv/Parser.hs
--- a/Data/Csv/Parser.hs
+++ b/Data/Csv/Parser.hs
@@ -6,7 +6,7 @@
 --  * Empty lines are ignored.
 --
 --  * Non-escaped fields may contain any characters except
---    double-quotes, commas, carriage returns, and newlines
+--    double-quotes, commas, carriage returns, and newlines.
 --
 --  * Escaped fields may contain any characters (but double-quotes
 --    need to be escaped).
@@ -45,10 +45,18 @@
 -- | Options that controls how data is decoded. These options can be
 -- used to e.g. decode tab-separated data instead of comma-separated
 -- data.
+--
+-- To avoid having your program stop compiling when new fields are
+-- added to 'DecodeOptions', create option records by overriding
+-- values in 'defaultDecodeOptions'. Example:
+--
+-- > myOptions = defaultDecodeOptions {
+-- >       decDelimiter = fromIntegral (ord '\t')
+-- >     }
 data DecodeOptions = DecodeOptions
     { -- | Field delimiter.
       decDelimiter  :: {-# UNPACK #-} !Word8
-    }
+    } deriving (Eq, Show)
 
 -- | Decoding options for parsing CSV files.
 defaultDecodeOptions :: DecodeOptions
diff --git a/Data/Csv/Streaming.hs b/Data/Csv/Streaming.hs
--- a/Data/Csv/Streaming.hs
+++ b/Data/Csv/Streaming.hs
@@ -5,9 +5,24 @@
 -- space. The API also allows you to ignore type conversion errors on
 -- a per-record basis.
 module Data.Csv.Streaming
-    ( Records(..)
+    (
+    -- * Usage example
+    -- $example
+
+    -- * Stream representation
+    -- $stream-representation
+      Records(..)
+
+    -- * Decoding records
+    -- $typeconversion
+
+    -- ** Index-based record conversion
+    -- $indexbased
     , decode
     , decodeWith
+
+    -- ** Name-based record conversion
+    -- $namebased
     , decodeByName
     , decodeByNameWith
     ) where
@@ -27,6 +42,38 @@
 import Data.Csv.Parser
 import Data.Csv.Types
 
+-- $example
+--
+-- A short usage example:
+--
+-- > for_ (decode False "John,27\r\nJane,28\r\n") $ \ (name, age :: Int) ->
+-- >     putStrLn $ name ++ " is " ++ show age ++ " years old"
+--
+-- N.B. The 'Foldable' instance, which is used above, skips records
+-- that failed to convert. If you don't want this behavior, work
+-- directly with the 'Cons' and 'Nil' constructors.
+
+-- $stream-representation
+--
+-- A stream of records is represented as a (lazy) list that may
+-- contain errors.
+
+-- $typeconversion
+--
+-- Just like in the case of non-streaming decoding, there are two ways
+-- to convert CSV records to and from and user-defined data types:
+-- index-based conversion and name-based conversion.
+
+-- $indexbased
+--
+-- See documentation on index-based conversion in "Data.Csv" for more
+-- information.
+
+-- $namebased
+--
+-- See documentation on name-based conversion in "Data.Csv" for more
+-- information.
+
 -- | A stream of parsed records. If type conversion failed for the
 -- record, the error is returned as @'Left' errMsg@.
 data Records a
@@ -39,6 +86,7 @@
     | Nil (Maybe String) BL.ByteString
     deriving (Eq, Functor, Show)
 
+-- | Skips records that failed to convert.
 instance Foldable Records where
     foldr = foldrRecords
 #if MIN_VERSION_base(4,6,0)
diff --git a/cassava.cabal b/cassava.cabal
--- a/cassava.cabal
+++ b/cassava.cabal
@@ -1,5 +1,5 @@
 Name:                cassava
-Version:             0.2.0.0
+Version:             0.2.1.0
 Synopsis:            A CSV parsing and encoding library
 Description:
   A CSV parsing and encoding library optimized for ease of use and high
diff --git a/examples/StreamingIndexBasedDecode.hs b/examples/StreamingIndexBasedDecode.hs
new file mode 100644
--- /dev/null
+++ b/examples/StreamingIndexBasedDecode.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+import qualified Data.ByteString.Lazy as BL
+import Data.Csv.Streaming
+import Data.Foldable (for_)
+
+main :: IO ()
+main = do
+    csvData <- BL.readFile "salaries.csv"
+    -- N.B. The Foldable instance skips records that failed to
+    -- convert.
+    for_ (decode False csvData) $ \ (name, salary :: Int) ->
+        putStrLn $ name ++ " earns " ++ show salary ++ " dollars"
