diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,17 @@
+0.4
+===
+
+  * Supported GHC 8.0.1.
+
+  * On GHC 7.8 and newer, as a secutiry measure, all declared variables are unset by the end of
+    a successful parse.  If you want to keep the variable in the environment, use the `keep` modifier.
+    (https://github.com/supki/envparse/pull/7)
+
+0.3.4
+=====
+
+  * Added `splitOn`.
+
 0.3.3
 =====
 
diff --git a/envparse.cabal b/envparse.cabal
--- a/envparse.cabal
+++ b/envparse.cabal
@@ -1,5 +1,5 @@
 name:                envparse
-version:             0.3.3
+version:             0.4
 synopsis:            Parse environment variables
 description:
   Here's a simple example of a program that uses @envparse@'s parser:
@@ -53,7 +53,7 @@
 category:            System
 build-type:          Simple
 cabal-version:       >= 1.10
-tested-with:         GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3
+tested-with:         GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1
 extra-source-files:
   README.markdown
   CHANGELOG.markdown
@@ -69,7 +69,7 @@
 source-repository this
   type:     git
   location: https://github.com/supki/envparse
-  tag:      0.3.3
+  tag:      0.4
 
 library
   default-language:
@@ -82,15 +82,14 @@
     src
   exposed-modules:
     Env
+    Env.Internal.Error
+    Env.Internal.Free
+    Env.Internal.Help
+    Env.Internal.Val
+    Env.Internal.Parser
   if impl(ghc >= 7.10)
     exposed-modules:
       Env.Generic
-  other-modules:
-    Env.Error
-    Env.Free
-    Env.Help
-    Env.Parse
-    Env.Val
   ghc-options:
     -Wall
 
@@ -104,6 +103,7 @@
       >= 4.6 && < 5
     , containers
     , hspec
+    , text
   hs-source-dirs:
     src
     test
@@ -112,5 +112,6 @@
   other-modules:
     EnvSpec
     Env.GenericSpec
+    Env.Internal.ParserSpec
   ghc-options:
     -Wall
diff --git a/src/Env.hs b/src/Env.hs
--- a/src/Env.hs
+++ b/src/Env.hs
@@ -60,6 +60,7 @@
   , Reader
   , str
   , nonempty
+  , splitOn
   , auto
   , def
   , helpDef
@@ -68,6 +69,8 @@
   , Flag
   , HasHelp
   , help
+  , HasKeep
+  , keep
   , Help.helpDoc
   , Error(..)
   , Error.AsUnset(..)
@@ -83,20 +86,23 @@
 
 import           Control.Applicative
 import           Control.Monad ((>=>), (<=<))
-import           Data.Foldable (asum)
+import           Data.Foldable (asum, for_)
 #if __GLASGOW_HASKELL__ < 710
 import           Data.Monoid (Monoid(..), (<>))
 #else
 import           Data.Monoid ((<>))
 #endif
 import           System.Environment (getEnvironment)
+#if __GLASGOW_HASKELL__ >= 708
+import           System.Environment (unsetEnv)
+#endif
 import           System.Exit (exitFailure)
 import qualified System.IO as IO
 
-import qualified Env.Help as Help
-import           Env.Parse
-import           Env.Error (Error)
-import qualified Env.Error as Error
+import qualified Env.Internal.Help as Help
+import           Env.Internal.Parser
+import           Env.Internal.Error (Error)
+import qualified Env.Internal.Error as Error
 
 -- $re-exports
 -- External functions that may be useful to the consumer of the library
@@ -120,11 +126,18 @@
 --
 -- Use this if simply dying on failure (the behavior of 'parse') is inadequate for your needs.
 parseOr :: (String -> IO a) -> (Help.Info Error -> Help.Info e) -> Parser e b -> IO (Either a b)
-parseOr f g p =
-  traverseLeft (f . Help.helpInfo (g Help.defaultInfo) p) . parsePure p =<< getEnvironment
+parseOr onFailure helpMod parser = do
+  b <- fmap (parsePure parser) getEnvironment
+#if __GLASGOW_HASKELL__ >= 708
+  for_ b $ \_ ->
+    eachUnsetVar parser unsetEnv
+#endif
+  traverseLeft (onFailure . Help.helpInfo (helpMod Help.defaultInfo) parser) b
 
 die :: String -> IO a
-die m = do IO.hPutStrLn IO.stderr m; exitFailure
+die m =
+  do IO.hPutStrLn IO.stderr m; exitFailure
 
 traverseLeft :: Applicative f => (a -> f b) -> Either a t -> f (Either b t)
-traverseLeft f = either (fmap Left . f) (pure . Right)
+traverseLeft f =
+  either (fmap Left . f) (pure . Right)
diff --git a/src/Env/Error.hs b/src/Env/Error.hs
deleted file mode 100644
--- a/src/Env/Error.hs
+++ /dev/null
@@ -1,64 +0,0 @@
--- | This module contains an extensible error infrastructure.
---
--- Each kind of errors gets a separate type class which encodes
--- a 'Prism' (roughly a getter and a constructor). The 'Reader's, then,
--- have the constraints for precisely the set of errors they can return.
-module Env.Error
-  ( Error(..)
-  , AsUnset(..)
-  , AsEmpty(..)
-  , AsUnread(..)
-  ) where
-
-
--- | The type of errors returned by @envparse@'s 'Reader's. These fall into 3
--- categories:
---
---   * Variables that are unset in the environment.
---   * Variables whose value is empty.
---   * Variables whose value cannot be parsed using the 'Read' instance.
-data Error
-  = UnsetError
-  | EmptyError
-  | UnreadError String
-    deriving (Show, Eq)
-
--- | The class of types that contain and can be constructed from
--- the error returned from parsing unset variables.
-class AsUnset e where
-  unset :: e
-  tryUnset :: e -> Maybe ()
-
-instance AsUnset Error where
-  unset = UnsetError
-  tryUnset err =
-    case err of
-      UnsetError -> Just ()
-      _ -> Nothing
-
--- | The class of types that contain and can be constructed from
--- the error returned from parsing variables whose value is empty.
-class AsEmpty e where
-  empty :: e
-  tryEmpty :: e -> Maybe ()
-
-instance AsEmpty Error where
-  empty = EmptyError
-  tryEmpty err =
-    case err of
-      EmptyError -> Just ()
-      _ -> Nothing
-
--- | The class of types that contain and can be constructed from
--- the error returned from parsing variable whose value cannot
--- be parsed using the 'Read' instance.
-class AsUnread e where
-  unread :: String -> e
-  tryUnread :: e -> Maybe String
-
-instance AsUnread Error where
-  unread = UnreadError
-  tryUnread err =
-    case err of
-      UnreadError msg -> Just msg
-      _ -> Nothing
diff --git a/src/Env/Free.hs b/src/Env/Free.hs
deleted file mode 100644
--- a/src/Env/Free.hs
+++ /dev/null
@@ -1,89 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE GADTs #-}
-{-# LANGUAGE RankNTypes #-}
-{-# LANGUAGE ScopedTypeVariables #-}
--- | @Alt F@ is the free 'Alternative' functor on @F@
-module Env.Free
-  ( Alt(..)
-  , liftAlt
-  , runAlt
-  , foldAlt
-  , hoistAlt
-  -- * Debug
-  , inspect
-  ) where
-
-#if __GLASGOW_HASKELL__ < 710
-import Control.Applicative (Applicative(..))
-#endif
-import Control.Applicative (Alternative(..))
-#if __GLASGOW_HASKELL__ < 710
-import Data.Monoid (Monoid(..))
-#endif
-
-
-data Alt f a where
-  Nope :: Alt f a
-  Pure :: a -> Alt f a
-  Ap   :: Alt f (a -> b) -> Alt f a -> Alt f b
-  Alt  :: Alt f a -> Alt f a -> Alt f a
-  Lift :: f a -> Alt f a
-
--- | Print the free structure
-inspect :: Alt f a -> String
-inspect Nope      = "Nope"
-inspect (Pure _)  = "Pure _"
-inspect (Ap f x)  = concat ["(", inspect f, ") <*> (", inspect x, ")"]
-inspect (Alt x y) = concat ["(", inspect x, ") <|> (", inspect y, ")"]
-inspect (Lift _)  = "Lift _"
-
-instance Functor f => Functor (Alt f) where
-  fmap _ Nope      = Nope
-  fmap f (Pure a)  = Pure (f a)
-  fmap f (Ap a v)  = Ap (fmap (f .) a) v
-  fmap f (Alt a b) = Alt (fmap f a) (fmap f b)
-  fmap f (Lift a)  = Lift (fmap f a)
-
-instance Functor f => Applicative (Alt f) where
-  pure = Pure
-  (<*>) = Ap
-
-instance Functor f => Alternative (Alt f) where
-  empty = Nope
-  (<|>) = Alt
-
-
-liftAlt :: f a -> Alt f a
-liftAlt = Lift
-
-runAlt :: forall f g a. Alternative g => (forall x. f x -> g x) -> Alt f a -> g a
-runAlt u = go where
-  go  :: Alt f b -> g b
-  go Nope      = empty
-  go (Pure a)  = pure a
-  go (Ap f x)  = go f <*> go x
-  go (Alt s t) = go s <|> go t
-  go (Lift x)  = u x
-
-foldAlt :: Monoid p => (forall a. f a -> p) -> Alt f b -> p
-foldAlt f = unMon . runAlt (Mon . f)
-
-hoistAlt :: forall f g b. Functor g => (forall a. f a -> g a) -> Alt f b -> Alt g b
-hoistAlt nat = runAlt (Lift . nat)
-
-
--- | The 'Alternative' functor induced by the 'Monoid'
-newtype Mon m a = Mon
-  { unMon :: m
-  } deriving (Show, Eq)
-
-instance Functor (Mon m) where
-  fmap _ (Mon a) = Mon a
-
-instance Monoid m => Applicative (Mon m) where
-  pure _ = Mon mempty
-  Mon x <*> Mon y = Mon (mappend x y)
-
-instance Monoid m => Alternative (Mon m) where
-  empty = Mon mempty
-  Mon x <|> Mon y = Mon (mappend x y)
diff --git a/src/Env/Generic.hs b/src/Env/Generic.hs
--- a/src/Env/Generic.hs
+++ b/src/Env/Generic.hs
@@ -1,6 +1,10 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DefaultSignatures #-}
 {-# LANGUAGE DeriveTraversable #-}
+#if __GLASGOW_HASKELL__ < 800
+{-# LANGUAGE ExplicitNamespaces #-}
+#endif
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE LambdaCase #-}
@@ -91,7 +95,11 @@
 module Env.Generic
   ( Record(..)
   , Field(..)
+#if __GLASGOW_HASKELL__ < 800
   , (?)(..)
+#else
+  , type (?)(..)
+#endif
   , G.Generic
   ) where
 
@@ -138,7 +146,7 @@
     fmap G.M1 . gr
 
 -- Constant values are converted to 'Env.Parser's using their 'Field' instance.
-instance (Env.AsUnset e, Field e a) => GRecord e (G.K1 i a) where
+instance Field e a => GRecord e (G.K1 i a) where
   gr State {stateVar} =
     fmap G.K1 (field stateVar Nothing)
 
@@ -158,7 +166,17 @@
   gr x =
     fmap G.L1 (gr x) <|> fmap G.R1 (gr x)
 
+#if __GLASGOW_HASKELL__ < 800
+type family Type x :: ConType where
+  Type G.NoSelector = 'Plain
+  Type x = 'Record
+
+data ConType = Plain | Record
+
 instance (G.Selector c, Type c ~ 'Record, GRecord e a) => GRecord e (G.S1 c a) where
+#else
+instance (G.Selector c, c ~ 'G.MetaSel ('Just x1) x2 x3 x4, GRecord e a) => GRecord e (G.S1 c a) where
+#endif
   gr state@State {statePrefix, stateCon} =
     fmap G.M1 (gr state {stateVar=statePrefix ++ suffix})
    where
@@ -179,12 +197,6 @@
   go2 "" = ""
   go2 (l:u:xs) | Char.isLower l && Char.isUpper u = l : '_' : u : go2 xs
   go2 (x:xs) = x : go2 xs
-
-type family Type x :: ConType where
-  Type G.NoSelector = 'Plain
-  Type x = 'Record
-
-data ConType = Plain | Record
 
 -- | Given a @Field e a@ instance, a value of the type @a@ can be parsed from an environment variable.
 -- If the parsing fails, a value of an error type @e@ is returned.
diff --git a/src/Env/Help.hs b/src/Env/Help.hs
deleted file mode 100644
--- a/src/Env/Help.hs
+++ /dev/null
@@ -1,134 +0,0 @@
-{-# LANGUAGE NamedFieldPuns #-}
-module Env.Help
-  ( helpInfo
-  , helpDoc
-  , Info
-  , ErrorHandler
-  , defaultInfo
-  , defaultErrorHandler
-  , header
-  , desc
-  , footer
-  , handleError
-  ) where
-
-import           Data.Foldable (asum)
-import qualified Data.List as List
-import qualified Data.Map as Map
-import           Data.Maybe (catMaybes, mapMaybe)
-import           Data.Ord (comparing)
-
-import           Env.Error (Error)
-import qualified Env.Error as Error
-import           Env.Free
-import           Env.Parse hiding (Mod)
-
-
-helpInfo :: Info e -> Parser e b -> [(String, e)] -> String
-helpInfo Info {infoHeader, infoDesc, infoFooter, infoHandleError} p errors =
-  List.intercalate "\n\n" $ catMaybes
-    [ infoHeader
-    , fmap (List.intercalate "\n" . splitWords 50) infoDesc
-    , Just (helpDoc p)
-    , fmap (List.intercalate "\n" . splitWords 50) infoFooter
-    ] ++ helpErrors infoHandleError errors
-
--- | A pretty-printed list of recognized environment variables suitable for usage messages
-helpDoc :: Parser e a -> String
-helpDoc p =
-  List.intercalate "\n" ("Available environment variables:\n" : helpParserDoc p)
-
-helpParserDoc :: Parser e a -> [String]
-helpParserDoc = concat . Map.elems . foldAlt (\v -> Map.singleton (varfName v) (helpVarfDoc v)) . unParser
-
-helpVarfDoc :: VarF e a -> [String]
-helpVarfDoc VarF { varfName, varfHelp, varfHelpDef } =
-  case varfHelp of
-    Nothing -> [indent 2 varfName]
-    Just h
-      | k > 15    -> indent 2 varfName : map (indent 25) (splitWords 30 t)
-      | otherwise ->
-          case zipWith indent (23 - k : repeat 25) (splitWords 30 t) of
-            (x : xs) -> (indent 2 varfName ++ x) : xs
-            []       -> [indent 2 varfName]
-     where k = length varfName
-           t = maybe h (\s -> h ++ " (default: " ++ s ++")") varfHelpDef
-
-splitWords :: Int -> String -> [String]
-splitWords n = go [] 0 . words
- where
-  go acc _ [] = prep acc
-  go acc k (w : ws)
-    | k + z < n = go (w : acc) (k + z) ws
-    | z > n     = prep acc ++ case splitAt n w of (w', w'') -> w' : go [] 0 (w'' : ws)
-    | otherwise = prep acc ++ go [w] z ws
-   where
-    z = length w
-
-  prep []  = []
-  prep acc = [unwords (reverse acc)]
-
-indent :: Int -> String -> String
-indent n s = replicate n ' ' ++ s
-
-helpErrors :: ErrorHandler e -> [(String, e)] -> [String]
-helpErrors _       [] = []
-helpErrors handler fs =
-  [ "Parsing errors:"
-  , List.intercalate "\n" (mapMaybe (uncurry handler) (List.sortBy (comparing varName) fs))
-  ]
-
--- | Parser's metadata
-data Info e = Info
-  { infoHeader      :: Maybe String
-  , infoDesc        :: Maybe String
-  , infoFooter      :: Maybe String
-  , infoHandleError :: ErrorHandler e
-  }
-
--- | Given a variable name and an error value, try to produce a useful error message
-type ErrorHandler e = String -> e -> Maybe String
-
-defaultInfo :: Info Error
-defaultInfo = Info
-  { infoHeader = Nothing
-  , infoDesc = Nothing
-  , infoFooter = Nothing
-  , infoHandleError = defaultErrorHandler
-  }
-
--- | Set the help text header (it usually includes the application's name and version)
-header :: String -> Info e -> Info e
-header h i = i {infoHeader=Just h}
-
--- | Set the short description
-desc :: String -> Info e -> Info e
-desc h i = i {infoDesc=Just h}
-
--- | Set the help text footer (it usually includes examples)
-footer :: String -> Info e -> Info e
-footer h i = i {infoFooter=Just h}
-
--- | An error handler
-handleError :: ErrorHandler e -> Info x -> Info e
-handleError handler i = i {infoHandleError=handler}
-
--- | The default error handler
-defaultErrorHandler :: (Error.AsUnset e, Error.AsEmpty e, Error.AsUnread e) => ErrorHandler e
-defaultErrorHandler name err =
-  asum [handleUnsetError name err, handleEmptyError name err, handleUnreadError name err]
-
-handleUnsetError :: Error.AsUnset e => ErrorHandler e
-handleUnsetError name =
-  fmap (\() -> indent 2 (name ++ " is unset")) . Error.tryUnset
-
-handleEmptyError :: Error.AsEmpty e => ErrorHandler e
-handleEmptyError name =
-  fmap (\() -> indent 2 (name ++ " is empty")) . Error.tryEmpty
-
-handleUnreadError :: Error.AsUnread e => ErrorHandler e
-handleUnreadError name =
-  fmap (\val -> indent 2 (name ++ " has value " ++ val ++ " that cannot be parsed")) . Error.tryUnread
-
-varName :: (String, e) -> String
-varName (n, _) = n
diff --git a/src/Env/Internal/Error.hs b/src/Env/Internal/Error.hs
new file mode 100644
--- /dev/null
+++ b/src/Env/Internal/Error.hs
@@ -0,0 +1,64 @@
+-- | This module contains an extensible error infrastructure.
+--
+-- Each kind of errors gets a separate type class which encodes
+-- a 'Prism' (roughly a getter and a constructor). The 'Reader's, then,
+-- have the constraints for precisely the set of errors they can return.
+module Env.Internal.Error
+  ( Error(..)
+  , AsUnset(..)
+  , AsEmpty(..)
+  , AsUnread(..)
+  ) where
+
+
+-- | The type of errors returned by @envparse@'s 'Reader's. These fall into 3
+-- categories:
+--
+--   * Variables that are unset in the environment.
+--   * Variables whose value is empty.
+--   * Variables whose value cannot be parsed using the 'Read' instance.
+data Error
+  = UnsetError
+  | EmptyError
+  | UnreadError String
+    deriving (Show, Eq)
+
+-- | The class of types that contain and can be constructed from
+-- the error returned from parsing unset variables.
+class AsUnset e where
+  unset :: e
+  tryUnset :: e -> Maybe ()
+
+instance AsUnset Error where
+  unset = UnsetError
+  tryUnset err =
+    case err of
+      UnsetError -> Just ()
+      _ -> Nothing
+
+-- | The class of types that contain and can be constructed from
+-- the error returned from parsing variables whose value is empty.
+class AsEmpty e where
+  empty :: e
+  tryEmpty :: e -> Maybe ()
+
+instance AsEmpty Error where
+  empty = EmptyError
+  tryEmpty err =
+    case err of
+      EmptyError -> Just ()
+      _ -> Nothing
+
+-- | The class of types that contain and can be constructed from
+-- the error returned from parsing variable whose value cannot
+-- be parsed using the 'Read' instance.
+class AsUnread e where
+  unread :: String -> e
+  tryUnread :: e -> Maybe String
+
+instance AsUnread Error where
+  unread = UnreadError
+  tryUnread err =
+    case err of
+      UnreadError msg -> Just msg
+      _ -> Nothing
diff --git a/src/Env/Internal/Free.hs b/src/Env/Internal/Free.hs
new file mode 100644
--- /dev/null
+++ b/src/Env/Internal/Free.hs
@@ -0,0 +1,91 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+-- | @Alt F@ is the free 'Alternative' functor on @F@
+module Env.Internal.Free
+  ( Alt(..)
+  , liftAlt
+  , runAlt
+  , foldAlt
+  , hoistAlt
+  -- * Debug
+  , inspect
+  ) where
+
+#if __GLASGOW_HASKELL__ < 710
+import Control.Applicative (Applicative(..))
+#endif
+import Control.Applicative (Alternative(..))
+#if __GLASGOW_HASKELL__ < 710
+import Data.Monoid (Monoid(..))
+#endif
+
+
+data Alt f a where
+  Nope :: Alt f a
+  Pure :: a -> Alt f a
+  Ap   :: Alt f (a -> b) -> Alt f a -> Alt f b
+  Alt  :: Alt f a -> Alt f a -> Alt f a
+  Lift :: f a -> Alt f a
+
+-- | Print the free structure
+inspect :: Alt f a -> String
+inspect Nope      = "Nope"
+inspect (Pure _)  = "Pure _"
+inspect (Ap f x)  = concat ["(", inspect f, ") <*> (", inspect x, ")"]
+inspect (Alt x y) = concat ["(", inspect x, ") <|> (", inspect y, ")"]
+inspect (Lift _)  = "Lift _"
+
+instance Functor f => Functor (Alt f) where
+  fmap _ Nope      = Nope
+  fmap f (Pure a)  = Pure (f a)
+  fmap f (Ap a v)  = Ap (fmap (f .) a) v
+  fmap f (Alt a b) = Alt (fmap f a) (fmap f b)
+  fmap f (Lift a)  = Lift (fmap f a)
+
+instance Functor f => Applicative (Alt f) where
+  pure = Pure
+  (<*>) = Ap
+
+instance Functor f => Alternative (Alt f) where
+  empty = Nope
+  (<|>) = Alt
+
+
+liftAlt :: f a -> Alt f a
+liftAlt = Lift
+
+runAlt :: forall f g a. Alternative g => (forall x. f x -> g x) -> Alt f a -> g a
+runAlt u = go where
+  go  :: Alt f b -> g b
+  go Nope      = empty
+  go (Pure a)  = pure a
+  go (Ap f x)  = go f <*> go x
+  go (Alt s t) = go s <|> go t
+  go (Lift x)  = u x
+
+foldAlt :: Monoid p => (forall a. f a -> p) -> Alt f b -> p
+foldAlt f =
+  unMon . runAlt (Mon . f)
+
+hoistAlt :: forall f g b. Functor g => (forall a. f a -> g a) -> Alt f b -> Alt g b
+hoistAlt nat =
+  runAlt (Lift . nat)
+
+
+-- | The 'Alternative' functor induced by the 'Monoid'
+newtype Mon m a = Mon
+  { unMon :: m
+  } deriving (Show, Eq)
+
+instance Functor (Mon m) where
+  fmap _ (Mon a) = Mon a
+
+instance Monoid m => Applicative (Mon m) where
+  pure _ = Mon mempty
+  Mon x <*> Mon y = Mon (mappend x y)
+
+instance Monoid m => Alternative (Mon m) where
+  empty = Mon mempty
+  Mon x <|> Mon y = Mon (mappend x y)
diff --git a/src/Env/Internal/Help.hs b/src/Env/Internal/Help.hs
new file mode 100644
--- /dev/null
+++ b/src/Env/Internal/Help.hs
@@ -0,0 +1,137 @@
+{-# LANGUAGE NamedFieldPuns #-}
+module Env.Internal.Help
+  ( helpInfo
+  , helpDoc
+  , Info
+  , ErrorHandler
+  , defaultInfo
+  , defaultErrorHandler
+  , header
+  , desc
+  , footer
+  , handleError
+  ) where
+
+import           Data.Foldable (asum)
+import qualified Data.List as List
+import qualified Data.Map as Map
+import           Data.Maybe (catMaybes, mapMaybe)
+import           Data.Ord (comparing)
+
+import           Env.Internal.Error (Error)
+import qualified Env.Internal.Error as Error
+import           Env.Internal.Free
+import           Env.Internal.Parser hiding (Mod)
+
+
+helpInfo :: Info e -> Parser e b -> [(String, e)] -> String
+helpInfo Info {infoHeader, infoDesc, infoFooter, infoHandleError} p errors =
+  List.intercalate "\n\n" $ catMaybes
+    [ infoHeader
+    , fmap (List.intercalate "\n" . splitWords 50) infoDesc
+    , Just (helpDoc p)
+    , fmap (List.intercalate "\n" . splitWords 50) infoFooter
+    ] ++ helpErrors infoHandleError errors
+
+-- | A pretty-printed list of recognized environment variables suitable for usage messages
+helpDoc :: Parser e a -> String
+helpDoc p =
+  List.intercalate "\n" ("Available environment variables:\n" : helpParserDoc p)
+
+helpParserDoc :: Parser e a -> [String]
+helpParserDoc =
+  concat . Map.elems . foldAlt (\v -> Map.singleton (varfName v) (helpVarfDoc v)) . unParser
+
+helpVarfDoc :: VarF e a -> [String]
+helpVarfDoc VarF {varfName, varfHelp, varfHelpDef} =
+  case varfHelp of
+    Nothing -> [indent 2 varfName]
+    Just h
+      | k > 15    -> indent 2 varfName : map (indent 25) (splitWords 30 t)
+      | otherwise ->
+          case zipWith indent (23 - k : repeat 25) (splitWords 30 t) of
+            (x : xs) -> (indent 2 varfName ++ x) : xs
+            []       -> [indent 2 varfName]
+     where k = length varfName
+           t = maybe h (\s -> h ++ " (default: " ++ s ++")") varfHelpDef
+
+splitWords :: Int -> String -> [String]
+splitWords n =
+  go [] 0 . words
+ where
+  go acc _ [] = prep acc
+  go acc k (w : ws)
+    | k + z < n = go (w : acc) (k + z) ws
+    | z > n     = prep acc ++ case splitAt n w of (w', w'') -> w' : go [] 0 (w'' : ws)
+    | otherwise = prep acc ++ go [w] z ws
+   where
+    z = length w
+
+  prep []  = []
+  prep acc = [unwords (reverse acc)]
+
+indent :: Int -> String -> String
+indent n s =
+  replicate n ' ' ++ s
+
+helpErrors :: ErrorHandler e -> [(String, e)] -> [String]
+helpErrors _       [] = []
+helpErrors handler fs =
+  [ "Parsing errors:"
+  , List.intercalate "\n" (mapMaybe (uncurry handler) (List.sortBy (comparing varName) fs))
+  ]
+
+-- | Parser's metadata
+data Info e = Info
+  { infoHeader      :: Maybe String
+  , infoDesc        :: Maybe String
+  , infoFooter      :: Maybe String
+  , infoHandleError :: ErrorHandler e
+  }
+
+-- | Given a variable name and an error value, try to produce a useful error message
+type ErrorHandler e = String -> e -> Maybe String
+
+defaultInfo :: Info Error
+defaultInfo = Info
+  { infoHeader = Nothing
+  , infoDesc = Nothing
+  , infoFooter = Nothing
+  , infoHandleError = defaultErrorHandler
+  }
+
+-- | Set the help text header (it usually includes the application's name and version)
+header :: String -> Info e -> Info e
+header h i = i {infoHeader=Just h}
+
+-- | Set the short description
+desc :: String -> Info e -> Info e
+desc h i = i {infoDesc=Just h}
+
+-- | Set the help text footer (it usually includes examples)
+footer :: String -> Info e -> Info e
+footer h i = i {infoFooter=Just h}
+
+-- | An error handler
+handleError :: ErrorHandler e -> Info x -> Info e
+handleError handler i = i {infoHandleError=handler}
+
+-- | The default error handler
+defaultErrorHandler :: (Error.AsUnset e, Error.AsEmpty e, Error.AsUnread e) => ErrorHandler e
+defaultErrorHandler name err =
+  asum [handleUnsetError name err, handleEmptyError name err, handleUnreadError name err]
+
+handleUnsetError :: Error.AsUnset e => ErrorHandler e
+handleUnsetError name =
+  fmap (\() -> indent 2 (name ++ " is unset")) . Error.tryUnset
+
+handleEmptyError :: Error.AsEmpty e => ErrorHandler e
+handleEmptyError name =
+  fmap (\() -> indent 2 (name ++ " is empty")) . Error.tryEmpty
+
+handleUnreadError :: Error.AsUnread e => ErrorHandler e
+handleUnreadError name =
+  fmap (\val -> indent 2 (name ++ " has value " ++ val ++ " that cannot be parsed")) . Error.tryUnread
+
+varName :: (String, e) -> String
+varName (n, _) = n
diff --git a/src/Env/Internal/Parser.hs b/src/Env/Internal/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Env/Internal/Parser.hs
@@ -0,0 +1,271 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE ViewPatterns #-}
+module Env.Internal.Parser
+  ( Parser(..)
+  , VarF(..)
+  , parsePure
+  , eachUnsetVar
+  , Mod(..)
+  , prefixed
+  , var
+  , Var(..)
+  , defaultVar
+  , Reader
+  , str
+  , nonempty
+  , splitOn
+  , auto
+  , def
+  , helpDef
+  , showDef
+  , flag
+  , switch
+  , Flag
+  , HasHelp
+  , help
+  , HasKeep
+  , keep
+  ) where
+
+import           Control.Applicative
+import           Control.Arrow (left)
+import           Control.Monad ((<=<))
+import           Data.Foldable (for_)
+import           Data.Map (Map)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+#if __GLASGOW_HASKELL__ < 710
+import           Data.Monoid (Monoid(..))
+#endif
+import           Data.String (IsString(..))
+
+import           Env.Internal.Free
+import qualified Env.Internal.Error as Error
+import           Env.Internal.Val
+
+
+-- | Try to parse a pure environment
+parsePure :: Parser e a -> [(String, String)] -> Either [(String, e)] a
+parsePure (Parser p) (Map.fromList -> env) =
+  toEither (runAlt go p)
+ where
+  go v = maybe id (\d x -> x <|> pure d) (varfDef v) (fromEither (readVar v env))
+
+eachUnsetVar :: Applicative m => Parser e a -> (String -> m b) -> m ()
+eachUnsetVar Parser {unParser} =
+  for_ (foldAlt (\VarF {varfKeep, varfName} -> if varfKeep then Set.empty else Set.singleton varfName) unParser)
+
+readVar :: VarF e a -> Map String String -> Either [(String, e)] a
+readVar VarF {varfName, varfReader} =
+  left (pure . (\err -> (varfName, err))) . varfReader varfName
+
+
+-- | An environment parser
+newtype Parser e a = Parser { unParser :: Alt (VarF e) a }
+    deriving (Functor)
+
+instance Applicative (Parser e) where
+  pure =
+    Parser . pure
+  Parser f <*> Parser x =
+    Parser (f <*> x)
+
+instance Alternative (Parser e) where
+  empty =
+    Parser empty
+  Parser f <|> Parser x =
+    Parser (f <|> x)
+
+-- | The string to prepend to the name of every declared environment variable
+prefixed :: String -> Parser e a -> Parser e a
+prefixed pre =
+  Parser . hoistAlt (\v -> v {varfName=pre ++ varfName v}) . unParser
+
+
+data VarF e a = VarF
+  { varfName    :: String
+  , varfReader  :: String -> Map String String -> Either e a
+  , varfHelp    :: Maybe String
+  , varfDef     :: Maybe a
+  , varfHelpDef :: Maybe String
+  , varfKeep    :: Bool
+  } deriving (Functor)
+
+liftVarF :: VarF e a -> Parser e a
+liftVarF =
+  Parser . liftAlt
+
+-- | An environment variable's value parser. Use @(<=<)@ and @(>=>)@ to combine these
+type Reader e a = String -> Either e a
+
+lookupVar :: Error.AsUnset e => String -> Map String String -> Either e String
+lookupVar name =
+  maybe (Left Error.unset) Right . Map.lookup name
+
+-- | Parse a particular variable from the environment
+--
+-- @
+-- >>> var 'str' \"EDITOR\" ('def' \"vim\" <> 'helpDef' show)
+-- @
+var :: Error.AsUnset e => Reader e a -> String -> Mod Var a -> Parser e a
+var r n (Mod f) =
+  liftVarF $ VarF
+    { varfName    = n
+    , varfReader  = \name -> r <=< lookupVar name
+    , varfHelp    = varHelp
+    , varfDef     = varDef
+    , varfHelpDef = varHelpDef <*> varDef
+    , varfKeep    = varKeep
+    }
+ where
+  Var {varHelp, varDef, varHelpDef, varKeep} = f defaultVar
+
+-- | A flag that takes the active value if the environment variable
+-- is set and non-empty and the default value otherwise
+--
+-- /Note:/ this parser never fails.
+flag
+  :: a -- ^ default value
+  -> a -- ^ active value
+  -> String -> Mod Flag a -> Parser e a
+flag f t n (Mod g) =
+  liftVarF $ VarF
+    { varfName    = n
+    , varfReader  = \name env ->
+        pure $ case (nonempty :: Reader Error.Error String) =<< lookupVar name env of
+          Left  _ -> f
+          Right _ -> t
+    , varfHelp    = flagHelp
+    , varfDef     = Just f
+    , varfHelpDef = Nothing
+    , varfKeep    = flagKeep
+    }
+ where
+  Flag {flagHelp, flagKeep} = g defaultFlag
+
+-- | A simple boolean 'flag'
+--
+-- /Note:/ this parser never fails.
+switch :: String -> Mod Flag Bool -> Parser e Bool
+switch =
+  flag False True
+
+-- | The trivial reader
+str :: IsString s => Reader e s
+str =
+  Right . fromString
+
+-- | The reader that accepts only non-empty strings
+nonempty :: (Error.AsEmpty e, IsString s) => Reader e s
+nonempty =
+  fmap fromString . go where go [] = Left Error.empty; go xs = Right xs
+
+-- | The reader that uses the 'Read' instance of the type
+auto :: (Error.AsUnread e, Read a) => Reader e a
+auto s =
+  case reads s of [(v, "")] -> Right v; _ -> Left (Error.unread (show s))
+
+-- | The reader that splits a string into a list of strings consuming the separator.
+splitOn :: Char -> Reader e [String]
+splitOn sep = Right . go
+ where
+  go [] = []
+  go xs = go' xs
+
+  go' xs =
+    case break (== sep) xs of
+      (ys, []) ->
+        ys : []
+      (ys, _ : zs) ->
+        ys : go' zs
+
+
+-- | This represents a modification of the properties of a particular 'Parser'.
+-- Combine them using the 'Monoid' instance.
+newtype Mod t a = Mod (t a -> t a)
+
+instance Monoid (Mod t a) where
+  mempty = Mod id
+  mappend (Mod f) (Mod g) = Mod (g . f)
+
+-- | Environment variable metadata
+data Var a = Var
+  { varHelp    :: Maybe String
+  , varHelpDef :: Maybe (a -> String)
+  , varDef     :: Maybe a
+  , varKeep    :: Bool
+  }
+
+defaultVar :: Var a
+defaultVar = Var
+  { varHelp    = Nothing
+  , varDef     = Nothing
+  , varHelpDef = Nothing
+  , varKeep    = defaultKeep
+  }
+
+defaultKeep :: Bool
+defaultKeep = False
+
+-- | The default value of the variable
+--
+-- /Note:/ specifying it means the parser won't ever fail.
+def :: a -> Mod Var a
+def d =
+  Mod (\v -> v {varDef=Just d})
+
+-- | Flag metadata
+data Flag a = Flag
+  { flagHelp   :: Maybe String
+  , flagKeep   :: Bool
+  }
+
+defaultFlag :: Flag a
+defaultFlag = Flag
+  { flagHelp = Nothing
+  , flagKeep = defaultKeep
+  }
+
+-- | Show the default value of the variable in help.
+helpDef :: (a -> String) -> Mod Var a
+helpDef d =
+  Mod (\v -> v {varHelpDef=Just d})
+
+-- | Use the 'Show' instance to show the default value of the variable in help.
+showDef :: Show a => Mod Var a
+showDef =
+  helpDef show
+
+
+-- | A class of things that can have a help message attached to them
+class HasHelp t where
+  setHelp :: String -> t a -> t a
+
+instance HasHelp Var where
+  setHelp h v = v {varHelp=Just h}
+
+instance HasHelp Flag where
+  setHelp h v = v {flagHelp=Just h}
+
+-- | Attach help text to the variable
+help :: HasHelp t => String -> Mod t a
+help =
+  Mod . setHelp
+
+-- | A class of things that can be still kept in an environment when the
+-- parsing has been completed.
+class HasKeep t where
+  setKeep :: t a -> t a
+
+instance HasKeep Var where
+  setKeep v = v {varKeep=True}
+
+instance HasKeep Flag where
+  setKeep v = v {flagKeep=True}
+
+-- | Keep a variable.
+keep :: HasKeep t => Mod t a
+keep =
+  Mod setKeep
diff --git a/src/Env/Internal/Val.hs b/src/Env/Internal/Val.hs
new file mode 100644
--- /dev/null
+++ b/src/Env/Internal/Val.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveFunctor #-}
+module Env.Internal.Val
+  ( Val(..)
+  , fromEither
+  , toEither
+  ) where
+
+import Control.Applicative
+#if __GLASGOW_HASKELL__ < 710
+import Data.Monoid (Monoid(..))
+#endif
+import Data.Monoid ((<>))
+
+
+-- | A type isomorphic to 'Either' with the accumulating 'Applicative' instance.
+data Val e a
+  = Err e
+  | Ok  a
+    deriving (Functor, Show, Eq)
+
+instance Monoid e => Applicative (Val e) where
+  pure = Ok
+
+  Err e <*> Err e' = Err (e <> e')
+  Err e <*> _      = Err e
+  _     <*> Err e' = Err e'
+  Ok  f <*> Ok  a  = Ok (f a)
+
+instance Monoid e => Alternative (Val e) where
+  empty = Err mempty
+
+  Err _ <|> Ok x = Ok x
+  x     <|> _    = x
+
+fromEither :: Either e a -> Val e a
+fromEither =
+  either Err Ok
+
+toEither :: Val e a -> Either e a
+toEither x =
+  case x of Err e -> Left e; Ok a -> Right a
diff --git a/src/Env/Parse.hs b/src/Env/Parse.hs
deleted file mode 100644
--- a/src/Env/Parse.hs
+++ /dev/null
@@ -1,203 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE DeriveFunctor #-}
-{-# LANGUAGE NamedFieldPuns #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE ViewPatterns #-}
-module Env.Parse
-  ( Parser(..)
-  , VarF(..)
-  , parsePure
-  , Mod(..)
-  , prefixed
-  , var
-  , Var(..)
-  , defaultVar
-  , Reader
-  , str
-  , nonempty
-  , auto
-  , def
-  , helpDef
-  , showDef
-  , flag
-  , switch
-  , Flag
-  , HasHelp
-  , help
-  ) where
-
-import           Control.Applicative
-import           Control.Arrow (left)
-import           Control.Monad ((<=<))
-import           Data.Map (Map)
-import qualified Data.Map as Map
-#if __GLASGOW_HASKELL__ < 710
-import           Data.Monoid (Monoid(..))
-#endif
-import           Data.String (IsString(..))
-
-import           Env.Free
-import qualified Env.Error as Error
-import           Env.Val
-
-
--- | Try to parse a pure environment
-parsePure :: Parser e b -> [(String, String)] -> Either [(String, e)] b
-parsePure (Parser p) (Map.fromList -> env) =
-  toEither (runAlt go p)
- where
-  go v = maybe id (\d x -> x <|> pure d) (varfDef v) (fromEither (readVar v env))
-
-readVar :: VarF e a -> Map String String -> Either [(String, e)] a
-readVar VarF {varfName, varfReader} =
-  left (pure . (\err -> (varfName, err))) . varfReader varfName
-
-
--- | An environment parser
-newtype Parser e a = Parser { unParser :: Alt (VarF e) a }
-    deriving (Functor)
-
-instance Applicative (Parser e) where
-  pure = Parser . pure
-  Parser f <*> Parser x = Parser (f <*> x)
-
-instance Alternative (Parser e) where
-  empty = Parser empty
-  Parser f <|> Parser x = Parser (f <|> x)
-
--- | The string to prepend to the name of every declared environment variable
-prefixed :: String -> Parser e a -> Parser e a
-prefixed pre =
-  Parser . hoistAlt (\v -> v { varfName = pre ++ varfName v }) . unParser
-
-
-data VarF e a = VarF
-  { varfName    :: String
-  , varfReader  :: String -> Map String String -> Either e a
-  , varfHelp    :: Maybe String
-  , varfDef     :: Maybe a
-  , varfHelpDef :: Maybe String
-  } deriving (Functor)
-
--- | An environment variable's value parser. Use @(<=<)@ and @(>=>)@ to combine these
-type Reader e a = String -> Either e a
-
-lookupVar :: Error.AsUnset e => String -> Map String String -> Either e String
-lookupVar name =
-  maybe (Left Error.unset) Right . Map.lookup name
-
--- | Parse a particular variable from the environment
---
--- @
--- >>> var 'str' \"EDITOR\" ('def' \"vim\" <> 'helpDef' show)
--- @
-var :: Error.AsUnset e => Reader e a -> String -> Mod Var a -> Parser e a
-var r n (Mod f) = Parser . liftAlt $ VarF
-  { varfName    = n
-  , varfReader  = \name -> r <=< lookupVar name
-  , varfHelp    = varHelp
-  , varfDef     = varDef
-  , varfHelpDef = varHelpDef <*> varDef
-  }
- where
-  Var { varHelp, varDef, varHelpDef } = f defaultVar
-
--- | A flag that takes the active value if the environment variable
--- is set and non-empty and the default value otherwise
---
--- /Note:/ this parser never fails.
-flag
-  :: a -- ^ default value
-  -> a -- ^ active value
-  -> String -> Mod Flag a -> Parser e a
-flag f t n (Mod g) = Parser . liftAlt $ VarF
-  { varfName    = n
-  , varfReader  = \name env ->
-      pure $ case (nonempty :: Reader Error.Error String) =<< lookupVar name env of
-        Left  _ -> f
-        Right _ -> t
-  , varfHelp    = flagHelp
-  , varfDef     = Just f
-  , varfHelpDef = Nothing
-  }
- where
-  Flag { flagHelp } = g defaultFlag
-
--- | A simple boolean 'flag'
---
--- /Note:/ this parser never fails.
-switch :: String -> Mod Flag Bool -> Parser e Bool
-switch = flag False True
-
--- | The trivial reader
-str :: IsString s => Reader e s
-str = Right . fromString
-
--- | The reader that accepts only non-empty strings
-nonempty :: (Error.AsEmpty e, IsString s) => Reader e s
-nonempty = fmap fromString . go where go [] = Left Error.empty; go xs = Right xs
-
--- | The reader that uses the 'Read' instance of the type
-auto :: (Error.AsUnread e, Read a) => Reader e a
-auto s = case reads s of [(v, "")] -> Right v; _ -> Left (Error.unread (show s))
-
-
--- | This represents a modification of the properties of a particular 'Parser'.
--- Combine them using the 'Monoid' instance.
-newtype Mod t a = Mod (t a -> t a)
-
-instance Monoid (Mod t a) where
-  mempty = Mod id
-  mappend (Mod f) (Mod g) = Mod (g . f)
-
--- | Environment variable metadata
-data Var a = Var
-  { varHelp    :: Maybe String
-  , varHelpDef :: Maybe (a -> String)
-  , varDef     :: Maybe a
-  }
-
-defaultVar :: Var a
-defaultVar = Var
-  { varHelp    = Nothing
-  , varDef     = Nothing
-  , varHelpDef = Nothing
-  }
-
--- | The default value of the variable
---
--- /Note:/ specifying it means the parser won't ever fail.
-def :: a -> Mod Var a
-def d = Mod (\v -> v { varDef = Just d })
-
--- | Flag metadata
-data Flag a = Flag
-  { flagHelp   :: Maybe String
-  }
-
-defaultFlag :: Flag a
-defaultFlag = Flag { flagHelp = Nothing }
-
--- | Show the default value of the variable in help.
-helpDef :: (a -> String) -> Mod Var a
-helpDef d = Mod (\v -> v { varHelpDef = Just d })
-
--- | Use the 'Show' instance to show the default value of the variable in help.
-showDef :: Show a => Mod Var a
-showDef =
-  helpDef show
-
-
--- | A class of things that can have a help message attached to them
-class HasHelp t where
-  setHelp :: String -> t a -> t a
-
-instance HasHelp Var where
-  setHelp h v = v { varHelp = Just h }
-
-instance HasHelp Flag where
-  setHelp h v = v { flagHelp = Just h }
-
--- | Attach help text to the variable
-help :: HasHelp t => String -> Mod t a
-help = Mod . setHelp
diff --git a/src/Env/Val.hs b/src/Env/Val.hs
deleted file mode 100644
--- a/src/Env/Val.hs
+++ /dev/null
@@ -1,40 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE DeriveFunctor #-}
-module Env.Val
-  ( Val(..)
-  , fromEither
-  , toEither
-  ) where
-
-import Control.Applicative
-#if __GLASGOW_HASKELL__ < 710
-import Data.Monoid (Monoid(..))
-#endif
-import Data.Monoid ((<>))
-
-
--- | A type isomorphic to 'Either' with the accumulating 'Applicative' instance.
-data Val e a
-  = Err e
-  | Ok  a
-    deriving (Functor, Show, Eq)
-
-instance Monoid e => Applicative (Val e) where
-  pure = Ok
-
-  Err e <*> Err e' = Err (e <> e')
-  Err e <*> _      = Err e
-  _     <*> Err e' = Err e'
-  Ok  f <*> Ok  a  = Ok (f a)
-
-instance Monoid e => Alternative (Val e) where
-  empty = Err mempty
-
-  Err _ <|> Ok x = Ok x
-  x     <|> _    = x
-
-fromEither :: Either e a -> Val e a
-fromEither = either Err Ok
-
-toEither :: Val e a -> Either e a
-toEither x = case x of Err e -> Left e; Ok a -> Right a
diff --git a/test/Env/Internal/ParserSpec.hs b/test/Env/Internal/ParserSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Env/Internal/ParserSpec.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Env.Internal.ParserSpec (spec) where
+
+import Control.Monad ((<=<))
+import Data.Text (Text)
+import Test.Hspec
+
+import Env.Internal.Error (Error)
+import Env.Internal.Parser
+
+
+spec :: Spec
+spec = do
+  describe "str" $ do
+    it "works with String" $
+      str "foo" `shouldBe` ok ("foo" :: String)
+
+    it "works with Text" $
+      str "foo" `shouldBe` ok ("foo" :: Text)
+
+  describe "splitOn" $ do
+    it "splits the string on a separator character" $
+      splitOn ',' "1,2,3" `shouldBe` ok ["1", "2", "3"]
+
+    it "splits the empty string into the empty list" $
+      splitOn ',' "" `shouldBe` ok []
+
+    it "creates an extra element for leading and trailing separators" $
+      splitOn ',' ",2," `shouldBe` ok ["", "2", ""]
+
+    it "is nicely composable " $
+      (mapM auto <=< splitOn ',') "1,2,3" `shouldBe` ok ([1, 2, 3] :: [Int])
+
+ok :: a -> Either Error a
+ok = Right
diff --git a/test/EnvSpec.hs b/test/EnvSpec.hs
--- a/test/EnvSpec.hs
+++ b/test/EnvSpec.hs
@@ -9,6 +9,9 @@
 import           Data.Monoid (mempty)
 #endif
 import           Prelude hiding (pi)
+#if __GLASGOW_HASKELL__ >= 708
+import           System.Environment (lookupEnv, setEnv)
+#endif
 import           Test.Hspec
 import           Text.Read (readMaybe)
 
@@ -82,6 +85,31 @@
         p (prefixed "pre" (prefixed "pro" (var str "morphism" mempty)))
        `shouldBe`
         Just "zygohistomorphic"
+
+#if __GLASGOW_HASKELL__ >= 708
+    it "unsets parsed variables" $ do
+      setEnv "FOO" "4"
+      setEnv "BAR" "7"
+      parse (header "hi") (liftA2 (+) (var auto "FOO" (help "a")) (var auto "BAR" (help "b"))) `shouldReturn` (11 :: Int)
+      lookupEnv "FOO" `shouldReturn` Nothing
+      lookupEnv "BAR" `shouldReturn` Nothing
+
+    context "some variables are marked as kept" $
+      it "does not unset them" $ do
+        setEnv "FOO" "4"
+        setEnv "BAR" "7"
+        parse (header "hi") (liftA2 (+) (var auto "FOO" (help "a" <> keep)) (var auto "BAR" (help "b"))) `shouldReturn` (11 :: Int)
+        lookupEnv "FOO" `shouldReturn` Just "4"
+        lookupEnv "BAR" `shouldReturn` Nothing
+
+    context "parsing fails" $
+      it "does not unset any variables" $ do
+        setEnv "FOO" "4"
+        setEnv "BAR" "bar"
+        parse (header "hi") (liftA2 (+) (var auto "FOO" (help "a" <> keep)) (var auto "BAR" (help "b"))) `shouldThrow` anyException
+        lookupEnv "FOO" `shouldReturn` Just "4"
+        lookupEnv "BAR" `shouldReturn` Just "bar"
+#endif
 
 
 greaterThan5 :: AsUnread e => Reader e Int
