diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,13 @@
+## Version 0.5.1.0
+
+ * Add `FromField`/`ToField` instance for `Natural` (#141,#142)
+ * Add `FromField`/`ToField` instances for `Scientific` (#143,#144)
+ * Add support for modifying Generics-based instances (adding
+   `Options`, `defaultOptions`, `fieldLabelModifier`,
+   `genericParseRecord`, `genericToRecord`, `genericToNamedRecord`,
+   `genericHeaderOrder`) (#139,#140)
+ * Documentation improvements
+
 ## Version 0.5.0.0
 
 ### Semantic changes
diff --git a/Data/Csv.hs b/Data/Csv.hs
--- a/Data/Csv.hs
+++ b/Data/Csv.hs
@@ -1,6 +1,7 @@
--- | This module implements encoding and decoding of CSV data. The
--- implementation is RFC 4180 compliant, with the following
--- extensions:
+-- | This module implements encoding and decoding
+-- of [comma-separated values (CSV)](https://en.wikipedia.org/wiki/Comma-separated_values)
+-- data. The implementation is [RFC 4180](https://tools.ietf.org/html/rfc4180)
+-- compliant, with the following extensions:
 --
 --  * Empty lines are ignored.
 --
@@ -93,6 +94,26 @@
     -- $fieldconversion
     , FromField(..)
     , ToField(..)
+
+    -- ** 'Generic' record conversion
+    -- $genericconversion
+    , genericParseRecord
+    , genericToRecord
+    , genericParseNamedRecord
+    , genericToNamedRecord
+    , genericHeaderOrder
+
+    -- *** 'Generic' type conversion options
+    , Options
+    , defaultOptions
+    , fieldLabelModifier
+
+    -- *** 'Generic' type conversion class name
+    -- $genericconversionclass
+    , GFromRecord
+    , GToRecord
+    , GFromNamedRecord
+    , GToNamedRecordHeader
     ) where
 
 import Prelude hiding (lookup)
@@ -105,19 +126,23 @@
 --
 -- Encoding standard Haskell types:
 --
--- > >>> encode [("John" :: Text, 27), ("Jane", 28)]
--- > "John,27\r\nJane,28\r\n"
+-- >>> :set -XOverloadedStrings
+-- >>> import Data.Text (Text)
+-- >>> encode [("John" :: Text, 27 :: Int), ("Jane", 28)]
+-- "John,27\r\nJane,28\r\n"
 --
--- Since string literals are overloaded we have to supply a type
+-- Since we enabled the [-XOverloadedStrings extension](https://downloads.haskell.org/~ghc/8.2.1/docs/html/users_guide/glasgow_exts.html#overloaded-string-literals),
+-- string literals are polymorphic and we have to supply a type
 -- signature as the compiler couldn't deduce which string type (i.e.
--- 'String' or 'Text') we want to use. In most cases type inference
--- will infer the type from the context and you can omit type
--- signatures.
+-- 'String', 'Data.Text.Short.ShortText', or 'Data.Text.Text') we want to use. In most cases
+-- type inference will infer the type from the context and you can
+-- omit type signatures.
 --
 -- Decoding standard Haskell types:
 --
--- > >>> decode NoHeader "John,27\r\nJane,28\r\n" :: Either String (Vector (Text, Int))
--- > Right [("John",27),("Jane",28)]
+-- >>> import Data.Vector (Vector)
+-- >>> decode NoHeader "John,27\r\nJane,28\r\n" :: Either String (Vector (Text, Int))
+-- Right [("John",27),("Jane",28)]
 --
 -- We pass 'NoHeader' as the first argument to indicate that the CSV
 -- input data isn't preceded by a header.
@@ -137,50 +162,59 @@
 
 -- $example-indexed-instance
 --
--- Derived:
+-- "GHC.Generics"-derived:
 --
 -- > {-# LANGUAGE DeriveGeneric #-}
 -- >
+-- > import Data.Text    (Text)
+-- > import GHC.Generics (Generic)
+-- >
 -- > data Person = Person { name :: !Text , salary :: !Int }
--- >     deriving Generic
+-- >     deriving (Generic, Show)
 -- >
 -- > instance FromRecord Person
 -- > instance ToRecord Person
 --
 -- Manually defined:
 --
+-- > import Control.Monad (mzero)
+-- >
 -- > data Person = Person { name :: !Text , salary :: !Int }
+-- >     deriving (Show)
 -- >
 -- > instance FromRecord Person where
 -- >     parseRecord v
 -- >         | length v == 2 = Person <$> v .! 0 <*> v .! 1
 -- >         | otherwise     = mzero
 -- > instance ToRecord Person where
--- >     toRecord (Person name age) = record [
--- >         toField name, toField age]
+-- >     toRecord (Person name' age') = record [
+-- >         toField name', toField age']
 --
 -- We can now use e.g. 'encode' and 'decode' to encode and decode our
 -- data type.
 --
 -- Encoding:
 --
--- > >>> encode [Person ("John" :: Text) 27]
--- > "John,27\r\n"
+-- >>> encode [Person ("John" :: Text) 27]
+-- "John,27\r\n"
 --
 -- Decoding:
 --
--- > >>> decode NoHeader "John,27\r\n" :: Either String (Vector Person)
--- > Right [Person {name = "John", salary = 27}]
+-- >>> decode NoHeader "John,27\r\n" :: Either String (Vector Person)
+-- Right [Person {name = "John", salary = 27}]
 --
 
 -- $example-named-instance
 --
--- Derived:
+-- "GHC.Generics"-derived:
 --
 -- > {-# LANGUAGE DeriveGeneric #-}
 -- >
+-- > import Data.Text    (Text)
+-- > import GHC.Generics (Generic)
+-- >
 -- > data Person = Person { name :: !Text , salary :: !Int }
--- >     deriving Generic
+-- >     deriving (Generic, Show)
 -- >
 -- > instance FromNamedRecord Person
 -- > instance ToNamedRecord Person
@@ -189,6 +223,7 @@
 -- Manually defined:
 --
 -- > data Person = Person { name :: !Text , salary :: !Int }
+-- >     deriving (Show)
 -- >
 -- > instance FromNamedRecord Person where
 -- >     parseNamedRecord m = Person <$> m .: "name" <*> m .: "salary"
@@ -204,13 +239,13 @@
 --
 -- Encoding:
 --
--- > >>> encodeDefaultOrderedByName [Person ("John" :: Text) 27]
--- > "name,salary\r\nJohn,27\r\n"
+-- >>> encodeDefaultOrderedByName [Person ("John" :: Text) 27]
+-- "name,salary\r\nJohn,27\r\n"
 --
 -- Decoding:
 --
--- > >>> decodeByName "name,salary\r\nJohn,27\r\n" :: Either String (Header, Vector Person)
--- > Right (["name","salary"],[Person {name = "John", salary = 27}])
+-- >>> decodeByName "name,salary\r\nJohn,27\r\n" :: Either String (Header, Vector Person)
+-- Right (["name","salary"],[Person {name = "John", salary = 27}])
 --
 
 -- $generic-processing
@@ -221,8 +256,9 @@
 -- parse a CSV file to a generic representation, just convert each
 -- record to a @'Vector' 'ByteString'@ value, like so:
 --
--- > >>> decode NoHeader "John,27\r\nJane,28\r\n" :: Either String (Vector (Vector ByteString))
--- > Right [["John","27"],["Jane","28"]]
+-- >>> import Data.ByteString (ByteString)
+-- >>> decode NoHeader "John,27\r\nJane,28\r\n" :: Either String (Vector (Vector ByteString))
+-- Right [["John","27"],["Jane","28"]]
 --
 -- As the example output above shows, all the fields are returned as
 -- uninterpreted 'ByteString' values.
@@ -325,3 +361,46 @@
 -- '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.
+
+-- $genericconversion
+--
+-- There may be times that you do not want to manually write out class
+-- instances for record conversion, but you can't rely upon the
+-- default instances (e.g. you can't create field names that match the
+-- actual column names in expected data).
+--
+-- For example, consider you have a type @MyType@ where you have
+-- prefixed certain columns with an underscore, but in the actual data
+-- they're not.  You can then write:
+--
+-- > myOptions :: Options
+-- > myOptions = defaultOptions { fieldLabelmodifier = rmUnderscore }
+-- >   where
+-- >     rmUnderscore ('_':str) = str
+-- >     rmUnderscore str       = str
+-- >
+-- > instance ToNamedRecord MyType where
+-- >   toNamedRecord = genericToNamedRecord myOptions
+-- >
+-- > instance FromNamedRecord MyType where
+-- >   parseNamedRecord = genericParseNamedRecord myOptions
+-- >
+-- > instance DefaultOrdered MyType where
+-- >   headerOrder = genericHeaderOrder myOptions
+
+-- $genericconversionclass
+--
+-- __NOTE__: Only the class /names/ are exposed in order to make it possible to write type signatures referring to these classes
+
+-- $setup
+-- >>> :set -XOverloadedStrings -XDeriveGeneric
+-- >>> import Data.Text (Text)
+-- >>> import Data.Vector (Vector)
+-- >>> import GHC.Generics (Generic)
+-- >>>
+-- >>> data Person = Person { name :: !Text, salary :: !Int } deriving (Generic, Show)
+-- >>> instance FromRecord Person
+-- >>> instance ToRecord Person
+-- >>> instance FromNamedRecord Person
+-- >>> instance ToNamedRecord Person
+-- >>> instance DefaultOrdered Person
diff --git a/Data/Csv/Builder.hs b/Data/Csv/Builder.hs
--- a/Data/Csv/Builder.hs
+++ b/Data/Csv/Builder.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 
 -- | Low-level bytestring builders. Most users want to use the more
--- type-safe 'Data.Csv.Incremental' module instead.
+-- type-safe "Data.Csv.Incremental" module instead.
 module Data.Csv.Builder
     (
     -- * Encoding single records and headers
diff --git a/Data/Csv/Conversion.hs b/Data/Csv/Conversion.hs
--- a/Data/Csv/Conversion.hs
+++ b/Data/Csv/Conversion.hs
@@ -38,6 +38,24 @@
     , ToRecord(..)
     , ToField(..)
 
+    -- ** Generic type conversion
+    , genericParseRecord
+    , genericToRecord
+    , genericParseNamedRecord
+    , genericToNamedRecord
+    , genericHeaderOrder
+
+    -- *** Generic type conversion options
+    , Options
+    , defaultOptions
+    , fieldLabelModifier
+
+    -- *** Generic type conversion class names
+    , GFromRecord
+    , GToRecord
+    , GFromNamedRecord
+    , GToNamedRecordHeader
+
     -- * Parser
     , Parser
     , runParser
@@ -66,11 +84,13 @@
 #if MIN_VERSION_bytestring(0,10,4)
 import qualified Data.ByteString.Short as SBS
 #endif
+import Data.List (intercalate)
 import Data.Hashable (Hashable)
 import qualified Data.HashMap.Lazy as HM
 import Data.Int (Int8, Int16, Int32, Int64)
 import qualified Data.IntMap as IM
 import qualified Data.Map as M
+import Data.Scientific (Scientific)
 import Data.Semigroup (Semigroup, (<>))
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
@@ -86,6 +106,7 @@
 import Data.Word (Word8, Word16, Word32, Word64)
 import GHC.Float (double2Float)
 import GHC.Generics
+import Numeric.Natural
 import Prelude hiding (lookup, takeWhile)
 
 import Data.Csv.Conversion.Internal
@@ -119,6 +140,41 @@
 ------------------------------------------------------------------------
 -- Index-based conversion
 
+-- | Options to customise how to generically encode\/decode your
+--   datatype to\/from CSV.
+--
+--   @since 0.5.1.0
+newtype Options = Options
+  { fieldLabelModifier :: String -> String
+    -- ^ How to convert Haskell field labels to CSV fields.
+    --
+    --   @since 0.5.1.0
+  }
+
+instance Show Options where
+  show (Options fld) =
+    "Options {"
+      ++ intercalate ","
+         [ "fieldLabelModifier =~ " ++ show sampleField ++ " -> " ++ show (fld sampleField)
+         ]
+      ++ "}"
+    where
+      sampleField = "_column_A"
+
+-- | Default conversion options.
+--
+--   @
+--   Options
+--   { 'fieldLabelModifier' = id
+--   }
+--   @
+--
+--   @since 0.5.1.0
+defaultOptions :: Options
+defaultOptions = Options
+  { fieldLabelModifier = id
+  }
+
 -- | A type that can be converted from a single CSV record, with the
 -- possibility of failure.
 --
@@ -145,8 +201,16 @@
     parseRecord :: Record -> Parser a
 
     default parseRecord :: (Generic a, GFromRecord (Rep a)) => Record -> Parser a
-    parseRecord r = to <$> gparseRecord r
+    parseRecord = genericParseRecord defaultOptions
 
+-- | A configurable CSV record parser.  This function applied to
+--   'defaultOptions' is used as the default for 'parseRecord' when the
+--   type is an instance of 'Generic'.
+--
+--   @since 0.5.1.0
+genericParseRecord :: (Generic a, GFromRecord (Rep a)) => Options -> Record -> Parser a
+genericParseRecord opts r = to <$> gparseRecord opts r
+
 -- | A type that can be converted to a single CSV record.
 --
 -- An example type and instance:
@@ -166,8 +230,16 @@
     toRecord :: a -> Record
 
     default toRecord :: (Generic a, GToRecord (Rep a) Field) => a -> Record
-    toRecord = V.fromList . gtoRecord . from
+    toRecord = genericToRecord defaultOptions
 
+-- | A configurable CSV record creator.  This function applied to
+--   'defaultOptions' is used as the default for 'toRecord' when the
+--   type is an instance of 'Generic'.
+--
+--   @since 0.5.1.0
+genericToRecord :: (Generic a, GToRecord (Rep a) Field) => Options -> a -> Record
+genericToRecord opts = V.fromList . gtoRecord opts . from
+
 instance FromField a => FromRecord (Only a) where
     parseRecord v
         | n == 1    = Only <$> unsafeIndex v 0
@@ -563,8 +635,16 @@
     parseNamedRecord :: NamedRecord -> Parser a
 
     default parseNamedRecord :: (Generic a, GFromNamedRecord (Rep a)) => NamedRecord -> Parser a
-    parseNamedRecord r = to <$> gparseNamedRecord r
+    parseNamedRecord = genericParseNamedRecord defaultOptions
 
+-- | A configurable CSV named record parser.  This function applied to
+--   'defaultOptions' is used as the default for 'parseNamedRecord'
+--   when the type is an instance of 'Generic'.
+--
+--   @since 0.5.1.0
+genericParseNamedRecord :: (Generic a, GFromNamedRecord (Rep a)) => Options -> NamedRecord -> Parser a
+genericParseNamedRecord opts r = to <$> gparseNamedRecord opts r
+
 -- | A type that can be converted to a single CSV record.
 --
 -- An example type and instance:
@@ -581,8 +661,17 @@
     default toNamedRecord ::
         (Generic a, GToRecord (Rep a) (B.ByteString, B.ByteString)) =>
         a -> NamedRecord
-    toNamedRecord = namedRecord . gtoRecord . from
+    toNamedRecord = genericToNamedRecord defaultOptions
 
+-- | A configurable CSV named record creator.  This function applied
+--   to 'defaultOptions' is used as the default for 'toNamedRecord' when
+--   the type is an instance of 'Generic'.
+--
+--   @since 0.5.1.0
+genericToNamedRecord :: (Generic a, GToRecord (Rep a) (B.ByteString, B.ByteString))
+                        => Options -> a -> NamedRecord
+genericToNamedRecord opts = namedRecord . gtoRecord opts . from
+
 -- | A type that has a default field order when converted to CSV. This
 -- class lets you specify how to get the headers to use for a record
 -- type that's an instance of 'ToNamedRecord'.
@@ -617,8 +706,17 @@
     default headerOrder ::
         (Generic a, GToNamedRecordHeader (Rep a)) =>
         a -> Header
-    headerOrder = V.fromList. gtoNamedRecordHeader . from
+    headerOrder = genericHeaderOrder defaultOptions
 
+-- | A configurable CSV header record generator.  This function
+--   applied to 'defaultOptions' is used as the default for
+--   'headerOrder' when the type is an instance of 'Generic'.
+--
+--   @since 0.5.1.0
+genericHeaderOrder :: (Generic a, GToNamedRecordHeader (Rep a))
+                      => Options -> a -> Header
+genericHeaderOrder opts = V.fromList. gtoNamedRecordHeader opts . from
+
 instance (FromField a, FromField b, Ord a) => FromNamedRecord (M.Map a b) where
     parseNamedRecord m = M.fromList <$>
                          (traverse parseBoth $ HM.toList m)
@@ -715,6 +813,22 @@
     toField = toField . T.encodeUtf8 . T.singleton
     {-# INLINE toField #-}
 
+-- | Accepts the same syntax as 'rational'. Ignores whitespace.
+--
+-- @since 0.5.1.0
+instance FromField Scientific where
+  parseField s = case parseOnly (ws *> A8.scientific <* ws) s of
+                   Left err -> typeError "Scientific" s (Just err)
+                   Right n  -> pure n
+  {-# INLINE parseField #-}
+
+-- | Uses decimal notation or scientific notation, depending on the number.
+--
+-- @since 0.5.1.0
+instance ToField Scientific where
+  toField = scientific
+  {-# INLINE toField #-}
+
 -- | Accepts same syntax as 'rational'. Ignores whitespace.
 instance FromField Double where
     parseField = parseDouble
@@ -814,6 +928,20 @@
     {-# INLINE toField #-}
 
 -- | Accepts an unsigned decimal number. Ignores whitespace.
+--
+-- @since 0.5.1.0
+instance FromField Natural where
+    parseField = parseUnsigned "Natural"
+    {-# INLINE parseField #-}
+
+-- | Uses decimal encoding.
+--
+-- @since 0.5.1.0
+instance ToField Natural where
+    toField = decimal
+    {-# INLINE toField #-}
+
+-- | Accepts an unsigned decimal number. Ignores whitespace.
 instance FromField Word8 where
     parseField = parseUnsigned "Word8"
     {-# INLINE parseField #-}
@@ -880,10 +1008,16 @@
 #endif
 
 #if MIN_VERSION_text_short(0,1,0)
+-- | Assumes UTF-8 encoding. Fails on invalid byte sequences.
+--
+-- @since 0.5.0.0
 instance FromField T.S.ShortText where
     parseField = maybe (fail "Invalid UTF-8 stream") pure . T.S.fromByteString
     {-# INLINE parseField #-}
 
+-- | Uses UTF-8 encoding.
+--
+-- @since 0.5.0.0
 instance ToField T.S.ShortText where
     toField = T.S.toByteString
     {-# INLINE toField #-}
@@ -1057,6 +1191,7 @@
     fail = Fail.fail
     {-# INLINE fail #-}
 
+-- | @since 0.5.0.0
 instance Fail.MonadFail Parser where
     fail msg = Parser $ \kf _ks -> kf msg
     {-# INLINE fail #-}
@@ -1085,6 +1220,7 @@
                                    in unParser a kf' ks
     {-# INLINE mplus #-}
 
+-- | @since 0.5.0.0
 instance Semigroup (Parser a) where
     (<>) = mplus
     {-# INLINE (<>) #-}
@@ -1119,116 +1255,118 @@
 -- Generics
 
 class GFromRecord f where
-    gparseRecord :: Record -> Parser (f p)
+    gparseRecord :: Options -> Record -> Parser (f p)
 
 instance GFromRecordSum f Record => GFromRecord (M1 i n f) where
-    gparseRecord v =
-        case (IM.lookup n gparseRecordSum) of
+    gparseRecord opts v =
+        case IM.lookup n (gparseRecordSum opts) of
             Nothing -> lengthMismatch n v
             Just p -> M1 <$> p v
       where
         n = V.length v
 
 class GFromNamedRecord f where
-    gparseNamedRecord :: NamedRecord -> Parser (f p)
+    gparseNamedRecord :: Options -> NamedRecord -> Parser (f p)
 
 instance GFromRecordSum f NamedRecord => GFromNamedRecord (M1 i n f) where
-    gparseNamedRecord v =
-        foldr (\f p -> p <|> M1 <$> f v) empty (IM.elems gparseRecordSum)
+    gparseNamedRecord opts v =
+        foldr (\f p -> p <|> M1 <$> f v) empty (IM.elems (gparseRecordSum opts))
 
 class GFromRecordSum f r where
-    gparseRecordSum :: IM.IntMap (r -> Parser (f p))
+    gparseRecordSum :: Options -> IM.IntMap (r -> Parser (f p))
 
 instance (GFromRecordSum a r, GFromRecordSum b r) => GFromRecordSum (a :+: b) r where
-    gparseRecordSum =
+    gparseRecordSum opts =
         IM.unionWith (\a b r -> a r <|> b r)
-            (fmap (L1 <$>) <$> gparseRecordSum)
-            (fmap (R1 <$>) <$> gparseRecordSum)
+            (fmap (L1 <$>) <$> gparseRecordSum opts)
+            (fmap (R1 <$>) <$> gparseRecordSum opts)
 
 instance GFromRecordProd f r => GFromRecordSum (M1 i n f) r where
-    gparseRecordSum = IM.singleton n (fmap (M1 <$>) f)
+    gparseRecordSum opts = IM.singleton n (fmap (M1 <$>) f)
       where
-        (n, f) = gparseRecordProd 0
+        (n, f) = gparseRecordProd opts 0
 
 class GFromRecordProd f r where
-    gparseRecordProd :: Int -> (Int, r -> Parser (f p))
+    gparseRecordProd :: Options -> Int -> (Int, r -> Parser (f p))
 
 instance GFromRecordProd U1 r where
-    gparseRecordProd n = (n, const (pure U1))
+    gparseRecordProd _ n = (n, const (pure U1))
 
 instance (GFromRecordProd a r, GFromRecordProd b r) => GFromRecordProd (a :*: b) r where
-    gparseRecordProd n0 = (n2, f)
+    gparseRecordProd opts n0 = (n2, f)
       where
         f r = (:*:) <$> fa r <*> fb r
-        (n1, fa) = gparseRecordProd n0
-        (n2, fb) = gparseRecordProd n1
+        (n1, fa) = gparseRecordProd opts n0
+        (n2, fb) = gparseRecordProd opts n1
 
 instance GFromRecordProd f Record => GFromRecordProd (M1 i n f) Record where
-    gparseRecordProd n = fmap (M1 <$>) <$> gparseRecordProd n
+    gparseRecordProd opts n = fmap (M1 <$>) <$> gparseRecordProd opts n
 
 instance FromField a => GFromRecordProd (K1 i a) Record where
-    gparseRecordProd n = (n + 1, \v -> K1 <$> parseField (V.unsafeIndex v n))
+    gparseRecordProd _ n = (n + 1, \v -> K1 <$> parseField (V.unsafeIndex v n))
 
 data Proxy s (f :: * -> *) a = Proxy
 
 instance (FromField a, Selector s) => GFromRecordProd (M1 S s (K1 i a)) NamedRecord where
-    gparseRecordProd n = (n + 1, \v -> (M1 . K1) <$> v .: name)
+    gparseRecordProd opts n = (n + 1, \v -> (M1 . K1) <$> v .: name)
       where
-        name = T.encodeUtf8 (T.pack (selName (Proxy :: Proxy s f a)))
+        name = T.encodeUtf8 (T.pack (fieldLabelModifier opts (selName (Proxy :: Proxy s f a))))
 
 
 class GToRecord a f where
-    gtoRecord :: a p -> [f]
+    gtoRecord :: Options -> a p -> [f]
 
 instance GToRecord U1 f where
-    gtoRecord U1 = []
+    gtoRecord _ U1 = []
 
 instance (GToRecord a f, GToRecord b f) => GToRecord (a :*: b) f where
-    gtoRecord (a :*: b) = gtoRecord a ++ gtoRecord b
+    gtoRecord opts (a :*: b) = gtoRecord opts a ++ gtoRecord opts b
 
 instance (GToRecord a f, GToRecord b f) => GToRecord (a :+: b) f where
-    gtoRecord (L1 a) = gtoRecord a
-    gtoRecord (R1 b) = gtoRecord b
+    gtoRecord opts (L1 a) = gtoRecord opts a
+    gtoRecord opts (R1 b) = gtoRecord opts b
 
 instance GToRecord a f => GToRecord (M1 D c a) f where
-    gtoRecord (M1 a) = gtoRecord a
+    gtoRecord opts (M1 a) = gtoRecord opts a
 
 instance GToRecord a f => GToRecord (M1 C c a) f where
-    gtoRecord (M1 a) = gtoRecord a
+    gtoRecord opts (M1 a) = gtoRecord opts a
 
 instance GToRecord a Field => GToRecord (M1 S c a) Field where
-    gtoRecord (M1 a) = gtoRecord a
+    gtoRecord opts (M1 a) = gtoRecord opts a
 
 instance ToField a => GToRecord (K1 i a) Field where
-    gtoRecord (K1 a) = [toField a]
+    gtoRecord _ (K1 a) = [toField a]
 
 instance (ToField a, Selector s) => GToRecord (M1 S s (K1 i a)) (B.ByteString, B.ByteString) where
-    gtoRecord m@(M1 (K1 a)) = [T.encodeUtf8 (T.pack (selName m)) .= toField a]
+    gtoRecord opts m@(M1 (K1 a)) = [name .= toField a]
+      where
+        name = T.encodeUtf8 (T.pack (fieldLabelModifier opts (selName m)))
 
 -- We statically fail on sum types and product types without selectors
 -- (field names).
 
 class GToNamedRecordHeader a
   where
-    gtoNamedRecordHeader :: a p -> [Name]
+    gtoNamedRecordHeader :: Options -> a p -> [Name]
 
 instance GToNamedRecordHeader U1
   where
-    gtoNamedRecordHeader _ = []
+    gtoNamedRecordHeader _ _ = []
 
 instance (GToNamedRecordHeader a, GToNamedRecordHeader b) =>
          GToNamedRecordHeader (a :*: b)
   where
-    gtoNamedRecordHeader _ = gtoNamedRecordHeader (undefined :: a p) ++
-                             gtoNamedRecordHeader (undefined :: b p)
+    gtoNamedRecordHeader opts _ = gtoNamedRecordHeader opts (undefined :: a p) ++
+                                  gtoNamedRecordHeader opts (undefined :: b p)
 
 instance GToNamedRecordHeader a => GToNamedRecordHeader (M1 D c a)
   where
-    gtoNamedRecordHeader _ = gtoNamedRecordHeader (undefined :: a p)
+    gtoNamedRecordHeader opts _ = gtoNamedRecordHeader opts (undefined :: a p)
 
 instance GToNamedRecordHeader a => GToNamedRecordHeader (M1 C c a)
   where
-    gtoNamedRecordHeader _ = gtoNamedRecordHeader (undefined :: a p)
+    gtoNamedRecordHeader opts _ = gtoNamedRecordHeader opts (undefined :: a p)
 
 -- | Instance to ensure that you cannot derive DefaultOrdered for
 -- constructors without selectors.
@@ -1239,12 +1377,12 @@
 instance DefaultOrdered (M1 S NoSelector a ()) => GToNamedRecordHeader (M1 S NoSelector a)
 #endif
   where
-    gtoNamedRecordHeader _ =
+    gtoNamedRecordHeader _ _ =
         error "You cannot derive DefaultOrdered for constructors without selectors."
 
 instance Selector s => GToNamedRecordHeader (M1 S s a)
   where
-    gtoNamedRecordHeader m
+    gtoNamedRecordHeader opts m
         | null name = error "Cannot derive DefaultOrdered for constructors without selectors"
-        | otherwise = [B8.pack (selName m)]
+        | otherwise = [B8.pack (fieldLabelModifier opts (selName m))]
       where name = selName m
diff --git a/Data/Csv/Conversion/Internal.hs b/Data/Csv/Conversion/Internal.hs
--- a/Data/Csv/Conversion/Internal.hs
+++ b/Data/Csv/Conversion/Internal.hs
@@ -1,17 +1,20 @@
 module Data.Csv.Conversion.Internal
     ( decimal
+    , scientific
     , realFloat
     ) where
 
 import Data.ByteString.Builder (Builder, toLazyByteString, word8, char8,
                                 string8, byteString)
 import qualified Data.ByteString.Builder.Prim as BP
+import Data.ByteString.Builder.Scientific (scientificBuilder)
 import Data.Array.Base (unsafeAt)
 import Data.Array.IArray
 import qualified Data.ByteString as B
 import Data.Char (ord)
 import Data.Int
 import Data.Monoid
+import Data.Scientific (Scientific)
 import Data.Word
 
 import Data.Csv.Util (toStrict)
@@ -98,6 +101,10 @@
 
 ------------------------------------------------------------------------
 -- Floating point numbers
+
+scientific :: Scientific -> B.ByteString
+scientific = toStrict . toLazyByteString . scientificBuilder
+{-# INLINE scientific #-}
 
 realFloat :: RealFloat a => a -> B.ByteString
 {-# SPECIALIZE realFloat :: Float -> B.ByteString #-}
diff --git a/Data/Csv/Incremental.hs b/Data/Csv/Incremental.hs
--- a/Data/Csv/Incremental.hs
+++ b/Data/Csv/Incremental.hs
@@ -344,6 +344,7 @@
       runBuilder :: Quoting -> Word8 -> Bool -> Builder.Builder
     }
 
+-- | @since 0.5.0.0
 instance Semigroup (Builder a) where
     Builder f <> Builder g =
         Builder $ \ qtng delim useCrlf ->
@@ -420,6 +421,7 @@
       runNamedBuilder :: Header -> Quoting -> Word8 -> Bool -> Builder.Builder
     }
 
+-- | @since 0.5.0.0
 instance Semigroup (NamedBuilder a) where
     NamedBuilder f <> NamedBuilder g =
         NamedBuilder $ \ hdr qtng delim useCrlf ->
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,98 @@
+# `cassava`: A CSV parsing and encoding library [![Hackage](https://img.shields.io/hackage/v/cassava.svg)](https://hackage.haskell.org/package/cassava) [![Build Status](https://travis-ci.org/hvr/cassava.svg)](https://travis-ci.org/hvr/cassava)
+
+**Please refer to the [package description](https://hackage.haskell.org/package/cassava#description) for an overview of `cassava`.**
+
+## Usage example
+
+Here's the two second crash course in using the library. Given a CSV file with this content:
+
+```csv
+John Doe,50000
+Jane Doe,60000
+```
+
+here's how you'd process it record-by-record:
+
+```haskell
+{-# LANGUAGE ScopedTypeVariables #-}
+
+import qualified Data.ByteString.Lazy as BL
+import Data.Csv
+import qualified Data.Vector as V
+
+main :: IO ()
+main = do
+    csvData <- BL.readFile "salaries.csv"
+    case decode NoHeader csvData of
+        Left err -> putStrLn err
+        Right v -> V.forM_ v $ \ (name, salary :: Int) ->
+            putStrLn $ name ++ " earns " ++ show salary ++ " dollars"
+```
+
+If you want to parse a file that includes a header, like this one
+
+```csv
+name,salary
+John Doe,50000
+Jane Doe,60000
+```
+
+use [`decodeByName`](https://hackage.haskell.org/package/cassava/docs/Data-Csv.html#v:decodeByName):
+
+```haskell
+{-# LANGUAGE OverloadedStrings #-}
+
+import Control.Applicative
+import qualified Data.ByteString.Lazy as BL
+import Data.Csv
+import qualified Data.Vector as V
+
+data Person = Person
+    { name   :: !String
+    , salary :: !Int
+    }
+
+instance FromNamedRecord Person where
+    parseNamedRecord r = Person <$> r .: "name" <*> r .: "salary"
+
+main :: IO ()
+main = do
+    csvData <- BL.readFile "salaries.csv"
+    case decodeByName csvData of
+        Left err -> putStrLn err
+        Right (_, v) -> V.forM_ v $ \ p ->
+            putStrLn $ name p ++ " earns " ++ show (salary p) ++ " dollars"
+```
+
+You can find more code examples in the [`examples/` folder](https://github.com/hvr/cassava/tree/master/examples) as well as smaller usage examples in the [`Data.Csv` module documentation](https://hackage.haskell.org/package/cassava/docs/Data-Csv.html).
+
+## Project Goals for `cassava`
+
+There's no end to what people consider CSV data. Most programs don't
+follow [RFC4180](https://tools.ietf.org/html/rfc4180) so one has to
+make a judgment call which contributions to accept.  Consequently, not
+everything gets accepted, because then we'd end up with a (slow)
+general purpose parsing library. There are plenty of those. The goal
+is to roughly accept what the Python
+[`csv`](https://docs.python.org/3/library/csv.html) module accepts.
+
+The Python `csv` module (which is implemented in C) is also considered
+the base-line for performance.  Adding options (e.g. the above
+mentioned parsing "flexibility") will have to be a trade off against
+performance. There's been complaints about performance in the past,
+therefore, if in doubt performance wins over features.
+
+Last but not least, it's important to keep the dependency footprint
+light, as each additional dependency incurs costs and risks in terms
+of additional maintenance overhead and loss of flexibility. So adding
+a new package dependency should only be done if that dependency is
+known to be a reliable package and there's a clear benefit which
+outweights the cost.
+
+## Further reading
+
+The primary API documentation for `cassava` is its Haddock documentation which can be found at http://hackage.haskell.org/package/cassava/docs/Data-Csv.html 
+
+Below are listed additional recommended third-party blogposts and tutorials
+
+ - [CSV encoding and decoding in Haskell with Cassava](https://www.stackbuilders.com/tutorials/haskell/csv-encoding-decoding/)
diff --git a/cassava.cabal b/cassava.cabal
--- a/cassava.cabal
+++ b/cassava.cabal
@@ -1,9 +1,33 @@
+cabal-version:       1.12
 Name:                cassava
-Version:             0.5.0.0
+Version:             0.5.1.0
 Synopsis:            A CSV parsing and encoding library
-Description:
-  A CSV parsing and encoding library optimized for ease of use and high
-  performance.
+Description: {
+
+@cassava@ is a library for parsing and encoding [RFC 4180](https://tools.ietf.org/html/rfc4180)
+compliant [comma-separated values (CSV)](https://en.wikipedia.org/wiki/Comma-separated_values) data,
+which is a textual line-oriented format commonly used for exchanging tabular data.
+.
+@cassava@'s API includes support for
+.
+- Index-based record-conversion
+- Name-based record-conversion
+- Typeclass directed conversion of fields and records
+- Built-in field-conversion instances for standard types
+- Customizable record-conversion instance derivation via GHC generics
+- Low-level [bytestring](https://hackage.haskell.org/package/bytestring) builders (see "Data.Csv.Builder")
+- Incremental decoding and encoding API (see "Data.Csv.Incremental")
+- Streaming API for constant-space decoding (see "Data.Csv.Streaming")
+.
+Moreover, this library is designed to be easy to use; for instance, here's a
+very simple example of encoding CSV data:
+.
+>>> Data.Csv.encode [("John",27),("Jane",28)]
+"John,27\r\nJane,28\r\n"
+.
+Please refer to the documentation in "Data.Csv" and the included [README](#readme) for more usage examples.
+
+}
 Homepage:            https://github.com/hvr/cassava
 License:             BSD3
 License-file:        LICENSE
@@ -15,16 +39,20 @@
 Maintainer:          hvr@gnu.org
 Category:            Text, Web, CSV
 Build-type:          Simple
-Cabal-version:       >=1.10
 Extra-source-files:  examples/*.hs,
-                     CHANGES.md
+                     CHANGES.md,
+                     README.md
 Tested-with:         GHC ==8.2.1, GHC ==8.0.2, GHC ==8.0.1, GHC ==7.10.3, GHC ==7.8.4, GHC ==7.6.3, GHC ==7.4.2
 
 ----------------------------------------------------------------------------
 
-flag pre-bytestring-0-10-4
-  description: bytestring < 0.10.4
+source-repository head
+  type:     git
+  location: https://github.com/hvr/cassava.git
 
+flag bytestring--LT-0_10_4
+  description: [bytestring](https://hackage.haskell.org/haskell/package/bytestring) < 0.10.4
+
 Library
   default-language: Haskell2010
   other-extensions:
@@ -65,18 +93,19 @@
 
   Build-depends:
     array >= 0.4 && < 0.6,
-    attoparsec >= 0.10.2 && < 0.14,
+    attoparsec >= 0.11.3.0 && < 0.14,
     base >= 4.5 && < 5,
     bytestring >= 0.9.2 && < 0.11,
     containers >= 0.4.2 && < 0.6,
     deepseq >= 1.1 && < 1.5,
     hashable < 1.3,
+    scientific >= 0.3.4.7 && < 0.4,
     text < 1.3,
     unordered-containers < 0.3,
     vector >= 0.8 && < 0.13,
     Only >= 0.1 && < 0.1.1
 
-  if flag(pre-bytestring-0-10-4)
+  if flag(bytestring--LT-0_10_4)
     build-depends: bytestring <  0.10.4
                  , bytestring-builder >= 0.10.8 && < 0.11
   else
@@ -87,6 +116,10 @@
   if impl(ghc < 7.6)
     build-depends: ghc-prim == 0.2.*
 
+  -- For Numeric.Natural
+  if impl(ghc < 7.10)
+    build-depends: nats >= 1 && < 1.2
+
   -- https://ghc.haskell.org/trac/ghc/wiki/Migration/8.0#Recommendationsforforward-compatibility
   if impl(ghc >= 8.0)
     ghc-options: -Wcompat -Wnoncanonical-monad-instances -Wnoncanonical-monadfail-instances
@@ -112,17 +145,27 @@
                , bytestring
                , cassava
                , hashable
+               , scientific
                , text
                , unordered-containers
                , vector
   -- extra dependencies not already used by lib:cassava
   build-depends: HUnit < 1.7
                , QuickCheck == 2.10.*
+               , quickcheck-instances >= 0.3.12 && < 0.4
                , test-framework == 0.8.*
                , test-framework-hunit == 0.3.*
                , test-framework-quickcheck2 == 0.3.*
 
   hs-source-dirs: tests
+
+  -- GHC.Generics lived in `ghc-prim` for GHC 7.2 & GHC 7.4 only
+  if impl(ghc < 7.6)
+    build-depends: ghc-prim == 0.2.*
+
+  -- For Numeric.Natural
+  if impl(ghc < 7.10)
+    build-depends: nats >= 1 && < 1.2
 
   -- https://ghc.haskell.org/trac/ghc/wiki/Migration/8.0#Recommendationsforforward-compatibility
   if impl(ghc >= 8.0)
diff --git a/tests/UnitTests.hs b/tests/UnitTests.hs
--- a/tests/UnitTests.hs
+++ b/tests/UnitTests.hs
@@ -1,6 +1,11 @@
-{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE CPP, DeriveGeneric, OverloadedStrings, ScopedTypeVariables #-}
 
+#if __GLASGOW_HASKELL__ >= 801
+{-# OPTIONS_GHC -Wno-orphans -Wno-unused-top-binds #-}
+#else
+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-unused-binds #-}
+#endif
+
 module Main
     ( main
     ) where
@@ -10,20 +15,28 @@
 import qualified Data.ByteString.Lazy.Char8 as BL8
 import qualified Data.HashMap.Strict as HM
 import Data.Int
+import Data.Scientific (Scientific)
 import qualified Data.Text as T
 import qualified Data.Text.Lazy as LT
 import qualified Data.Vector as V
 import qualified Data.Foldable as F
 import Data.Word
+import Numeric.Natural
+import GHC.Generics (Generic)
 import Test.HUnit
 import Test.Framework as TF
 import Test.Framework.Providers.HUnit as TF
 import Test.QuickCheck
+import Test.QuickCheck.Instances ()
 import Test.Framework.Providers.QuickCheck2 as TF
 
 import Data.Csv hiding (record)
 import qualified Data.Csv.Streaming as S
 
+#if !MIN_VERSION_base(4,8,0)
+import Control.Applicative ((<$>), (<*>))
+#endif
+
 ------------------------------------------------------------------------
 -- Parse tests
 
@@ -240,18 +253,6 @@
 ------------------------------------------------------------------------
 -- Conversion tests
 
-instance Arbitrary B.ByteString where
-    arbitrary = B.pack `fmap` arbitrary
-
-instance Arbitrary BL.ByteString where
-    arbitrary = BL.fromChunks `fmap` arbitrary
-
-instance Arbitrary T.Text where
-    arbitrary = T.pack `fmap` arbitrary
-
-instance Arbitrary LT.Text where
-    arbitrary = LT.fromChunks `fmap` arbitrary
-
 -- A single column with an empty string is indistinguishable from an
 -- empty line (which we will ignore.) We therefore encode at least two
 -- columns.
@@ -291,11 +292,13 @@
       , testProperty "Int16" (roundTrip :: Int16 -> Bool)
       , testProperty "Int32" (roundTrip :: Int32 -> Bool)
       , testProperty "Int64" (roundTrip :: Int64 -> Bool)
+      , testProperty "Natural" (roundTrip :: Natural -> Bool)
       , testProperty "Word" (roundTrip :: Word -> Bool)
       , testProperty "Word8" (roundTrip :: Word8 -> Bool)
       , testProperty "Word16" (roundTrip :: Word16 -> Bool)
       , testProperty "Word32" (roundTrip :: Word32 -> Bool)
       , testProperty "Word64" (roundTrip :: Word64 -> Bool)
+      , testProperty "Scientific" (roundTrip :: Scientific -> Bool)
       , testProperty "lazy ByteString"
         (roundTrip :: BL.ByteString -> Bool)
       , testProperty "Text" (roundTrip :: T.Text -> Bool)
@@ -321,25 +324,35 @@
                             "Cam yiyebilirim, bana zararı dokunmaz.")
       ]
     , testGroup "Partial Decodes"
-      [ testCase "Int"     (partialDecode
-                            (parseField "12.7" :: Parser Int))
-      , testCase "Word"    (partialDecode
-                            (parseField "12.7" :: Parser Word))
-      , testCase "Double"  (partialDecode
-                            (parseField "1.0+" :: Parser Double))
-      , testCase "Integer" (partialDecode
-                            (parseField "1e6"  :: Parser Integer))
+      [ testCase "Int"         (partialDecode
+                                (parseField "12.7" :: Parser Int))
+      , testCase "Natural"     (partialDecode
+                                (parseField "12.7" :: Parser Natural))
+      , testCase "Word"        (partialDecode
+                                (parseField "12.7" :: Parser Word))
+      , testCase "Scientific"  (partialDecode
+                                (parseField "1.0+" :: Parser Scientific))
+      , testCase "Double"      (partialDecode
+                                (parseField "1.0+" :: Parser Double))
+      , testCase "Integer"     (partialDecode
+                                (parseField "1e6"  :: Parser Integer))
       ]
     , testGroup "Space trimming"
-      [ testCase "_Int"     (expect (parseField " 12"     :: Parser Int)    12)
-      , testCase "Int_"     (expect (parseField "12 "     :: Parser Int)    12)
-      , testCase "_Int_"    (expect (parseField " 12 "    :: Parser Int)    12)
-      , testCase "_Word"    (expect (parseField " 12"     :: Parser Word)   12)
-      , testCase "Word_"    (expect (parseField "12 "     :: Parser Word)   12)
-      , testCase "_Word_"   (expect (parseField " 12 "    :: Parser Word)   12)
-      , testCase "_Double"  (expect (parseField " 1.2e1"  :: Parser Double) 12)
-      , testCase "Double_"  (expect (parseField "1.2e1 "  :: Parser Double) 12)
-      , testCase "_Double_" (expect (parseField " 1.2e1 " :: Parser Double) 12)
+      [ testCase "_Int"         (expect (parseField " 12"     :: Parser Int)        12)
+      , testCase "Int_"         (expect (parseField "12 "     :: Parser Int)        12)
+      , testCase "_Int_"        (expect (parseField " 12 "    :: Parser Int)        12)
+      , testCase "_Natural"     (expect (parseField " 12"     :: Parser Natural)    12)
+      , testCase "Natural_"     (expect (parseField "12 "     :: Parser Natural)    12)
+      , testCase "_Natural_"    (expect (parseField " 12 "    :: Parser Natural)    12)
+      , testCase "_Word"        (expect (parseField " 12"     :: Parser Word)       12)
+      , testCase "Word_"        (expect (parseField "12 "     :: Parser Word)       12)
+      , testCase "_Word_"       (expect (parseField " 12 "    :: Parser Word)       12)
+      , testCase "_Scientific"  (expect (parseField " 1.2e1"  :: Parser Scientific) 12)
+      , testCase "Scientific_"  (expect (parseField "1.2e1 "  :: Parser Scientific) 12)
+      , testCase "_Scientific_" (expect (parseField " 1.2e1 " :: Parser Scientific) 12)
+      , testCase "_Double"      (expect (parseField " 1.2e1"  :: Parser Double)     12)
+      , testCase "Double_"      (expect (parseField "1.2e1 "  :: Parser Double)     12)
+      , testCase "_Double_"     (expect (parseField " 1.2e1 " :: Parser Double)     12)
       ]
     ]
 
@@ -379,6 +392,53 @@
     expected = ["a" :: String]
 
 ------------------------------------------------------------------------
+-- Custom conversion option tests
+
+genericConversionTests :: [TF.Test]
+genericConversionTests =
+    [ testCase "headerOrder" (header ["column1", "column2", "column_3"] @=? hdrs)
+    , testCase "encode" (encodeDefaultOrderedByName sampleValues @?= sampleEncoding)
+    , testCase "decode" (Right (hdrs, V.fromList sampleValues) @=? decodeByName sampleEncoding)
+    , testProperty "roundTrip" rtProp
+    ]
+  where
+    hdrs = headerOrder (undefined :: SampleType)
+
+    sampleValues = [ SampleType ""      1     Nothing
+                   , SampleType "field" 99999 (Just 1.234)
+                   ]
+
+    sampleEncoding = "column1,column2,column_3\r\n,1,\r\nfield,99999,1.234\r\n"
+
+    rtProp :: [SampleType] -> Bool
+    rtProp vs = Right (hdrs, V.fromList vs)
+                == decodeByName (encodeDefaultOrderedByName vs)
+
+data SampleType = SampleType
+  { _column1  :: !T.Text
+  , column2   :: !Int
+  , _column_3 :: !(Maybe Double)
+  } deriving (Eq, Show, Read, Generic)
+
+sampleOptions :: Options
+sampleOptions = defaultOptions { fieldLabelModifier = rmUnderscore }
+  where
+    rmUnderscore ('_':str) = str
+    rmUnderscore str       = str
+
+instance ToNamedRecord SampleType where
+  toNamedRecord = genericToNamedRecord sampleOptions
+
+instance FromNamedRecord SampleType where
+  parseNamedRecord = genericParseNamedRecord sampleOptions
+
+instance DefaultOrdered SampleType where
+  headerOrder = genericHeaderOrder sampleOptions
+
+instance Arbitrary SampleType where
+  arbitrary = SampleType <$> arbitrary <*> arbitrary <*> arbitrary
+
+------------------------------------------------------------------------
 -- Test harness
 
 allTests :: [TF.Test]
@@ -387,6 +447,7 @@
            , testGroup "conversion" conversionTests
            , testGroup "custom-options" customOptionsTests
            , testGroup "instances" instanceTests
+           , testGroup "generic-conversions" genericConversionTests
            ]
 
 main :: IO ()
